POJ 3621 Sightseeing Cows (参数搜索 + 最短路(ms 原始的bellman tle, 用spfa才过)
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 6684 | Accepted: 2196 |
Description
Farmer John has decided to reward his cows for their hard work by taking them on a tour of the big city! The cows must decide how best to spend their free time.
Fortunately, they have a detailed city map showing the L (2 ≤ L ≤ 1000) major landmarks (conveniently numbered 1.. L) and the P (2 ≤ P ≤ 5000) unidirectional cow paths that join them. Farmer John will drive the cows to a starting landmark of their choice, from which they will walk along the cow paths to a series of other landmarks, ending back at their starting landmark where Farmer John will pick them up and take them back to the farm. Because space in the city is at a premium, the cow paths are very narrow and so travel along each cow path is only allowed in one fixed direction.
While the cows may spend as much time as they like in the city, they do tend to get bored easily. Visiting each new landmark is fun, but walking between them takes time. The cows know the exact fun values Fi (1 ≤ Fi ≤ 1000) for each landmark i.
The cows also know about the cowpaths. Cowpath i connects landmark L1i to L2i (in the direction L1i -> L2i ) and requires time Ti (1 ≤ Ti ≤ 1000) to traverse.
In order to have the best possible day off, the cows want to maximize the average fun value per unit time of their trip. Of course, the landmarks are only fun the first time they are visited; the cows may pass through the landmark more than once, but they do not perceive its fun value again. Furthermore, Farmer John is making the cows visit at least two landmarks, so that they get some exercise during their day off.
Help the cows find the maximum fun value per unit time that they can achieve.
Input
* Line 1: Two space-separated integers: L and P
* Lines 2..L+1: Line i+1 contains a single one integer: Fi
* Lines L+2..L+P+1: Line L+i+1 describes cow path i with three space-separated integers: L1i , L2i , and Ti
Output
* Line 1: A single number given to two decimal places (do not perform explicit rounding), the maximum possible average fun per unit time, or 0 if the cows cannot plan any trip at all in accordance with the above rules.
Sample Input
5 7 30 10 10 5 10 1 2 3 2 3 2 3 4 5 3 5 2 4 5 5 5 1 3 5 2 2
Sample Output
6.00
Source
【题目大意】
给出一个有向图,问求一个回路,使得回路上的点权之和/边权之和 最大。
【解题思路】
此题是对01分数规划的应用,那么首先明白01分数规划的思想.
01整数规划问题就是求解方程(a1*x1+a2*x2+..+an*xn)/(b1*x1+b2*x2+..+bn*xn)的最小值/最大值问题。其中xi = 0或1(i=1,2...n)
对于此类问题我们可以通过二分来求解很接近答案的近似值。我们可以先令:
(a1*x1+a2*x2+..+an*xn)/(b1*x1+b2*x2+..+bn*xn)=L,则我们可以将此式转换为:x1*(a1-b1*L)+x2*(a2-b2*L)+...xn*(an-bn*L)=0,我们先定义一个估计值val,如果这个值使得上面的式子小于0我们就可以知道val>L,如果上式等于0,则val = L;如果大于0,则val<L,显然我们可以采用二分的思想求解次问题。
对于此题,设happy[u]为点u的欢乐值,w[u][v]为u-->v的边权值。要求的是happy[1]+happy[2]+...+happy[n] / w[1][2]+...+w[n-1][n] = ans,设ans就是所求的最大值。则移项,ans*w[u][v] - happy[v] = 0 .我们重新构造一幅图,使得边权为happy[v] - ans*w[u][v]。用SPFA算法,二分枚举ans,判断是否存在负权回路,若存在,说明ans偏小了,则增大ans,若不存在,则减小ans。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int VM=1010; const int EM=5010; #define INF 10000000000000 struct Edge{ int to,nxt; int cap; }edge[EM<<1]; int n,m,happy[VM],head[VM],cnt; void addedge(int cu,int cv,int cw){ edge[cnt].to=cv; edge[cnt].cap=cw; edge[cnt].nxt=head[cu]; head[cu]=cnt++; } int vis[VM],count[VM]; double dis[VM]; bool SPFA(double mid){ queue<int> q; while(!q.empty()) q.pop(); for(int i=1;i<=n;i++){ dis[i]=INF; vis[i]=0; count[i]=0; } dis[1]=0; vis[1]=1; count[1]++; q.push(1); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; double newdis=mid*edge[i].cap-happy[v]; //新的边权值 if(dis[v]>dis[u]+newdis){ dis[v]=dis[u]+newdis; if(!vis[v]){ vis[v]=1; q.push(v); count[v]++; if(count[v]>=n) //有负权环路 return 0; } } } } return 1; //无负权环路 } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ cnt=0; memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) scanf("%d",&happy[i]); int u,v,w; while(m--){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } double ans=0,l=0,r=10000,mid; while(r-l>=0.001){ mid=(l+r)/2; if(SPFA(mid)) r=mid; else{ //有负权环路 ans=mid; l=mid; } } printf("%.2f\n",ans); } return 0; }