poj3621 Sightseeing Cows

相關題解參見這裏,這一類問題這裏都講到了,很全面。


/*
 * poj3621 AC 625ms
 * 所謂的01分數規劃+spfa判斷正(負)環+二分枚舉答案
 * 	   爲什麼就那麼慢,那個pascal的0ms是直接打印的答案吧,快得逆天了啊!
 * 經常會糾結這種問題:
 * 	   對一個任務選擇一種方案,存在不同的價值與成本,要求(價值/成本)最大的方案。
 *     
 *     事實上這都是 運籌學 的關於規劃的內容,所以要去看書了。
 * 	   
 * 	   除開規劃部分,spfa判斷正(負)環時要注意圖可能並不連通,所以要枚舉每一個點作爲起點,
 * 同時用vis[]記錄已經訪問過的點,避免重複計算,類似問題之前也遇到過。
 * */
#include<stdio.h>
#include<memory.h>
#include<queue>
#include<cmath>
#define INF 1000000000
using namespace std;

int n,p,f[1005];
struct EDGE
{
	int v,t,next;
}edge[5005];
int head[1005],tot,num[1005];
double d[1005];

inline void init()
{
//	FILE* fin;
//	fin = fopen("d.in","r");
	scanf("%d%d",&n,&p);
//	fscanf(fin,"%d%d",&n,&p);
	int i,j,k,m;
	memset(f,0,sizeof(f));
	for(i=1;i<=n;i++)
		scanf("%d",&f[i]);
//		fscanf(fin,"%d",&f[i]);
	tot = 0;
	memset(edge,0,sizeof(edge));
	memset(head,0,sizeof(head));
	for(i=1;i<=p;i++)
	{
		scanf("%d%d%d",&j,&k,&m);
//		fscanf(fin,"%d%d%d",&j,&k,&m);
		edge[++tot].v = k;
		edge[tot].t = m;
		edge[tot].next = head[j];
		head[j] = tot;
	}
//	fclose(fin);
	return;
}

inline bool spfa(double l)
{
	int i,j,k;
	bool inq[1005],vis[1005];
	double w;
	memset(vis,false,sizeof(vis));
	
	queue<int> q;
	for(j=1;j<=n;j++)			//注意,可能存在離散的點。	
	{
		if(vis[j]) continue;	//訪問過的點就跳過。
		memset(num,0,sizeof(num));
		memset(inq,false,sizeof(inq));
		memset(d,0,sizeof(d));
		d[j] = 0,q.push(j),inq[j] = true,num[j] = 1;
		while(!q.empty())
		{
			k = q.front();
			q.pop();
			inq[k] = false,vis[k] = true;
			for(i=head[k];i;i=edge[i].next)
			{
				w = f[k]-l*edge[i].t;		//將點權移到邊權上,爲什麼可以對應?
				if(d[edge[i].v]<d[k]+w)		//是判斷是否有正環
				{
					d[edge[i].v] = d[k]+w;
					if(!inq[edge[i].v])
					{
						num[edge[i].v]++;
						if(num[edge[i].v]>n) return true;	//有正環
						q.push(edge[i].v);
						inq[edge[i].v] = true;
					}
				}
			}
		}
	}
	return false;	//無正環
}

inline void solve()
{
	double mid,l,r,Eps = 0.000001;
	l = 0,r = 5000;
	do
	{
		mid = (l+r)/2;			//F(L) = a[i]-mid*b[i] mid即爲答案,函數爲減函數。
		if(spfa(mid)) 
			l = mid;			//有正環,則答案需要增大,移動下界。
		else r = mid;			//無正環,則答案需要減小,移動上界。
	}while(abs(r-l)>Eps);
	if(l>Eps) printf("%.2f\n",l); else printf("0\n");
}

	
int main()
{
	init();
	solve();	
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章