http://poj.org/problem?id=3621
这两天一直在复习代码,因为好久都不写东西了,而且转了语言,除了Dinic我什么都不会写……
题目大意:给你一个有向图,边有权(T),点有权(F),使求一个环,使得最大化∑(F)/∑(T)。
这是一个最优比率环问题,与上一个最有比率生成树问题相似,都是01分数规划的考点。
设 ans≥∑(F)/∑(T) , 有
∑(F)-∑(T)*ans≤0
∑(F-T*ans)≤0
我们可以二分ans,将图的边权变为 F-T*ans,用SPFA判断图中是否有负权回路。如果图中有负权回路,则当前回路中 ∑(F)-∑(T)*ans<0,继而推得∑(F)/∑(T)≤ans,为符合条件的解,ans需要向上二分;反之,ans需要向下二分。
难看的代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #include <cmath> #define DINF 10000000.0 #define mm 10000 #define mn 1001 #define eps 1e-3 using namespace std; queue<int> q; int x,y,n,m,cnt[mn]; double dist[mn],F[mn],z; bool vis[mn]; struct EDGE{ int pnt; double dist; EDGE *pre; EDGE (){} EDGE(int _pnt,double _dist,EDGE *_pre):pnt(_pnt),dist(_dist),pre(_pre){} }Edge[mm],*SP=Edge,*edge[mm]; inline void addedge(int a,int b,double c){ edge[a]=new(++SP)EDGE(b,c,edge[a]); } bool SPFA(double ans){ memset(cnt,0,sizeof(cnt)); for(int i=2;i<=n;i++) dist[i]=DINF; memset(vis,false,sizeof(vis)); while(!q.empty()) q.pop(); q.push(1);dist[1]=0; while(!q.empty()){ int i=q.front();q.pop();vis[i]=false; for(EDGE *j=edge[i];j;j=j->pre) if(dist[j->pnt]>dist[i]+ans*j->dist-F[j->pnt]){ dist[j->pnt]=dist[i]+ans*j->dist-F[j->pnt]; if(!vis[j->pnt]){ vis[j->pnt]=true; if(++cnt[j->pnt]==n) return true; q.push(j->pnt); } } } return false; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lf",&F[i]); for(int i=1;i<=m;i++){ scanf("%d%d%lf",&x,&y,&z); addedge(x,y,z); } double low=0,high=DINF; while(high-low>eps){ double mid=(low+high)/2.0; if(SPFA(mid)) low=mid; else high=mid; } printf("%.2lf\n",low); return 0; }