很有趣的一道题,看起来是个神奇网络流,其实我们只要知道网络的一些性质就可以做这道题了
因为题目要求流量守恒,所以我们其实是在网络中搬运流量,最终使得总费用减小,具体来说我们可以直接把这种“搬运”的关系建出来:
对于一条从$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-4,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 memset(vis,0,sizeof vis); 20 for(int i=1;i<=n;i++) dis[i]=inf; 21 dis[from]=0,inq[from]=true,qs.push(from); 22 while(!qs.empty()) 23 { 24 int tn=qs.front(); 25 inq[tn]=false,qs.pop(); 26 for(int i=p[tn];i;i=noww[i]) 27 if(dis[goal[i]]>dis[tn]+val[i]+x) 28 { 29 dis[goal[i]]=dis[tn]+val[i]+x; 30 if(!inq[goal[i]]) 31 { 32 inq[goal[i]]=true,qs.push(goal[i]); 33 if(++vis[goal[i]]>n) return false; 34 } 35 } 36 } 37 return true; 38 } 39 int main() 40 { 41 scanf("%d%d",&n,&m),n+=2,r=1500; 42 for(int i=1;i<=m;i++) 43 { 44 scanf("%d%d%lf%lf%d%lf",&t1,&t2,&d1,&d2,&t3,&d3); 45 if(t1==n-1) {from=t2; continue;} 46 if(t2==n-1) {from=t1; continue;} 47 link(t1,t2,d2+d3); if(t3) link(t2,t1,d1-d3); 48 } 49 while(r-l>eps) 50 { 51 double mid=(l+r)/2; 52 if(check(mid)) r=mid; 53 else l=mid; 54 } 55 printf("%.2lf",r); 56 return 0; 57 }