哈 学会最小费用最大流啦
思路是这样。
首先我们有一个贪心策略。如果我们每次找到单位费用和最短的一条增广路,那么显然我们可以把这条路添加到已有的流量里去——不管这条路的流量是多大,反正它能扩大现有流量,而且目前为止它是可以扩大流量的所有路径中单位花费最少的。
然后我们就把这条路填上。想想看当我们找不到这样一条路的时候会发生什么?
那就是没有增广路了。恭喜我们获得最小费用最大流。这是为什么呢?
首先没有任何一条增广路的时候我们肯定获得最大流没错
然后我们回顾我们扩展的方式。每次我们都选择了一条单位费用和最短的路径,也就是说,我们总是先把便宜的路尽可能利用完了,才去利用那些贵的。鉴于此,我们总是已经把更便宜的填满了,整张图上没有更便宜的路了,才去尝试探索更贵的。
那么这就是最小费用qwq
那这个思路怎么实现呢?
我们可以把边的长度设为单位费用,然后对每个点SPFA。这样SPFA算出来的每个点的距离就是可走增广路中从起点到这个点最少的单位费用和。
然后SPFA过程中顺便求路径上的流量。顺便记录用了哪些边 最后暴力回跳修改边的信息。
#include<cstdio> #include<cstring> #include<cctype> #include<cstdlib> #include<algorithm> #include<queue> #define maxn 5050 #define maxm 10050 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } inline int count(int i){ return i&1?i+1:i-1; } struct Edge{ int from,to,next,val,dis,flow; }edge[maxm*2]; int head[maxn],num; inline void addedge(int from,int to,int val,int dis){ edge[++num]=(Edge){from,to,head[from],val,dis,0}; head[from]=num; } inline void add(int from,int to,int val,int dis){ addedge(from,to,val,dis); addedge(to,from,0,-dis); } bool vis[maxn]; int pre[maxn]; int cost[maxn]; int dst[maxn]; struct Answer{ bool flag;long long flow;long long cost; }; Answer spfa(int Start,int End){ Answer ans=(Answer){0,0,0}; memset(dst,127/3,sizeof(dst)); dst[Start]=0; cost[Start]=0x7fffffff; queue<int> f; f.push(Start); while(!f.empty()){ int from=f.front(); f.pop(); vis[from]=0; for(int i=head[from];i;i=edge[i].next){ int to=edge[i].to; if(edge[i].val<=edge[i].flow||dst[to]<=dst[from]+edge[i].dis) continue; dst[to]=dst[from]+edge[i].dis; pre[to]=i; cost[to]=min(cost[from],edge[i].val-edge[i].flow); if(!vis[to]){ vis[to]=1; f.push(to); } } } if(dst[End]==dst[0]) return ans; ans.flag=1;ans.flow+=cost[End]; ans.cost+=(long long)dst[End]*cost[End]; int now=End; while(now!=Start){ int ret=pre[now]; edge[ret].flow+=cost[End]; edge[count(ret)].flow-=cost[End]; now=edge[ret].from; } return ans; } long long Flow,Cost; int main(){ int n=read(),m=read(),Start=read(),End=read(); for(int i=1;i<=m;++i){ int from=read(),to=read(),val=read(),dis=read(); add(from,to,val,dis); } while(1){ Answer ans=spfa(Start,End); if(!ans.flag) break; if(!ans.flow) break; Flow+=ans.flow; Cost+=ans.cost; } printf("%lld %lld",Flow,Cost); return 0; }