很有趣的一道题,看起来是个神奇网络流,其实我们只要知道网络的一些性质就可以做这道题了
因为题目要求流量守恒,所以我们其实是在网络中搬运流量,最终使得总费用减小,具体来说我们可以直接把这种“搬运”的关系建出来:
对于一条从$u$到$v$的边,从$u$向$v$连一条$b+d$的边,如果其上限不为零,再从$v$向$u$连一条$a-d$的边
那么得到的这张新图其实是描述了图中的费用流,一个合法的搬运方案就是一个环(转了一圈保证流量还是守恒的),然后有一个叫做消圈定理的东西:
消圈定理:残量网络里如果存在负费用环,那么当前流不是最小费用流。因为通过增加残量网络负权边的流量,减少正权边的流量,一定能得到另一个更优的可行流。
于是就判负环吧=。=
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=5005,M=3005; 7 const double eps=1e-6,inf=1e12; 8 int n,m,t1,t2,t3,cnt,last,from; 9 double val[2*M+N],dis[N],d1,d2,d3,l,r; 10 int p[N],noww[2*M+N],goal[2*M+N],inq[N],vis[N]; 11 queue<int> qs; 12 void link(int f,int t,double v) 13 { 14 noww[++cnt]=p[f],p[f]=cnt; 15 goal[cnt]=t,val[cnt]=v; 16 } 17 bool check(double x) 18 { 19 for(int i=1;i<=n;i++) 20 { 21 dis[i]=inf,vis[i]=0; 22 inq[i]=true,qs.push(i); 23 } 24 while(!qs.empty()) 25 { 26 int tn=qs.front(); 27 inq[tn]=false,qs.pop(); 28 for(int i=p[tn];i;i=noww[i]) 29 if(dis[goal[i]]>dis[tn]+val[i]+x) 30 { 31 dis[goal[i]]=dis[tn]+val[i]+x; 32 if(!inq[goal[i]]) 33 { 34 inq[goal[i]]=true,qs.push(goal[i]); 35 if(++vis[goal[i]]>n) return false; 36 } 37 } 38 } 39 return true; 40 } 41 int main() 42 { 43 scanf("%d%d",&n,&m),n+=2,r=1500; 44 for(int i=1;i<=m;i++) 45 { 46 scanf("%d%d%lf%lf%d%lf",&t1,&t2,&d1,&d2,&t3,&d3); 47 if((t1==n-1)||(t2==n-1)) continue; 48 link(t1,t2,d2+d3); if(t3) link(t2,t1,d1-d3); 49 } 50 while(r-l>eps) 51 { 52 double mid=(l+r)/2; 53 if(check(mid)) r=mid; 54 else l=mid; 55 } 56 printf("%.2lf",r); 57 return 0; 58 }