洛谷3288 SCOI21方伯伯運椰子(分數規劃+spfa)
阿新 • • 發佈:2018-12-22
紀念部落格又一次爆炸了
首先,對於本題中,我們可以發現,保證存在正整數解,就表示一定費用會降低。又因為一旦加大的流量,費用一定會變大,所以總流量一定是不變的
那麼我們這時候就需要考慮一個退流的過程
對於原圖每一條\(u->v,c>0\)的邊,我們在新圖中建一條\(v->u,價值是a-d\)
表示退這個流要花費的費用,相當於退流的過程
對於原圖任意一條\(u->v\)的邊,我們在新圖中建一條\(u->v,價值是b+d\)的邊,相當於擴流的過程
那麼只有成環的時候,才能滿足流量平衡這個條件。
正好和消圈定理相類似
所謂消圈定理 就是在某個流 f 中,如果其對應的殘餘網路沒有負圈(剩餘流量為 0 的邊視為不存在) 那它一定就是當前流量下的最小費用流。 反之亦然。 即「f 是最小費用流等價於其殘餘網路中沒有負圈」。
那根據題目要求的是個比例,那我們一定是隻修改最大的那個環就行。
那麼我們考慮分數規劃一下
二分\(mid <= max(\frac{x-y}{k})\)
\[mid\times k\le x-y\]
\[mid\times k + (y-x) \le 0\]
由於在一個環中,k就是這個環的大小,我們可以考慮把每個\(mid\)分配到每個邊,也就是轉化成了
每條邊的權值在原來新圖的基礎上\(+mid\),然後\(check\)是否存在負(0)環
這時候直接上\(spfa\)就好,
不過之前的問題轉化,還是很有難度啊
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<map> #include<set> #define mk makr_pair #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } const int maxn = 50010; const int maxm = 1e6+1e2; struct Node{ int u,v,a,b,c,d; }; Node a[maxn]; int point[maxn],nxt[maxm],to[maxm]; int inque[maxn]; int vis[maxn]; double val[maxm]; queue<int> q; int cnt,n,m; double dis[maxn]; int x[maxm],y[maxm]; double w[maxm]; double l=0,r=1e9; int tmp; double ans; bool flag; int s,t; void addedge(int x,int y,double w) { //cout<<x<<" "<<y<<" "<<w<<endl; nxt[++cnt]=point[x]; to[cnt]=y; val[cnt]=w; point[x]=cnt; } void spfa(int s) { while (!q.empty()) q.pop(); for (int i=1;i<=n;i++) dis[i]=1e9; memset(inque,0,sizeof(inque)); memset(vis,0,sizeof(vis)); dis[s]=0; inque[s]=1; q.push(s); while (!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; inque[x]++; if (inque[x]>=n+1) { flag=true; return; } //cout<<1<<endl; for (int i=point[x];i;i=nxt[i]) { int p = to[i]; if (dis[p]>=dis[x]+val[i]) { dis[p]=dis[x]+val[i]; if (!vis[p]) { q.push(p); vis[p]=1; } } } } } bool check(double mid) { cnt=0; flag=false; memset(point,0,sizeof(point)); for (int i=1;i<=tmp;i++) addedge(x[i],y[i],w[i]+mid); spfa(n-1); if (flag) return true; else return false; } int main() { n=read(),m=read(); n+=2; for (int i=1;i<=m;i++) { int u=read(),v=read(),a=read(),b=read(),c=read(),d=read(); ++tmp; x[tmp]=u; y[tmp]=v; w[tmp]=b+d; if (c>0) { ++tmp; x[tmp]=v; y[tmp]=u; w[tmp]=a-d; } } // cout<<check(103)<<endl; // return 0; while (r-l>1e-3){ double mid = (l+r)/2; if (check(mid)) ans=mid,l=mid; else r=mid; } printf("%.2lf\n",ans); return 0; }