时隔多日,又一模板收入其中:最小费用最大流。
概念引入
费用:对每条有向边新增的一个属性,费用表示每个经过这条边的一个单位流量将会添加这么多费用。
将抽象的问题具象化:在一张高速网中有 $n$ 个收费站,$m$ 条单向通行的道路,每走完一条道路就会到那边的收费站收费,收费价格会根据你走的哪条路收费,而不是固定的收费点就收固定的钱,这个也很合逻辑吧。然后一条道路最多同行 $w$ 辆车,也就是说这条路一旦走过超过 $w$ 辆车那这条路将不再放行,虽然不符合我们的认知但是他就这么规定了你也没办法嘛对吧。在 $n$ 个收费站有一个源收费站一个目的收费站,问最多有多少辆车能从源收费站到目的收费站,总费用最小的通行方案是多少。
解题步骤 乍一听好像没啥思路,但是从一个司机的角度去考虑,这就会变得简单了,因为对于一个司机来说,我的目标是到达目的收费站且使我花费最少。那么起点终点确定了,每条路收费确定了,收费站之间的道路也确定了,那么我以收费为边权计算我到终点的单源最短路不就可以了么,走单源最短路一定会使得我的花费最少,因为边权是费用,最短在某种意义上就是费用最少啦。
但是我们是不可能为每一个司机考虑的,我们可以搜出一条单源最短路,那么就很容易计算这条路上的总费用和最小流量。那么这一次我就相当于通过了,最小流量 辆车,费用相当于多了 最小流量$\times$ 总费用 。这里就跟网络流有点类似了,我求单源最短路的过程也可以视为找一条增广路。在结果保存之后我们依然要添加反向边,然后对应流量减少,反向边流量增加。但是这里我们还需要注意这个反向边费用是多少呢?其实很好理解,我一辆车过去,再回来,对车来说相当于没过去,也就没有花费虽然实际情况不是这样,所以我们添加反向边的时候要给路径长度为负边权。这样就决定了我们只能考虑某已死算法,虽然你可以给所有边权加上一个最大值使得每个边权为正,最后跑完 $\text{dijkstra}$ 之后再每条边减去这么多。但是实际我写出来并不行,也不知道为什么,但是听说国际公约:网络流不卡 $\text{Dinic}$ 算法,费用流不卡 $\text{spfa}$ 算法。
最后一点需要注意:一条边流量为 $0$ 之后,它应被视为断开。
标程 模板题:P3381
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <bits/stdc++.h> #define maxn 100005 #define int long long #define inf 0x3f3f3f3f3f3f3f3f using namespace std;typedef pair<int ,int > P;struct eee { int to; int w; int d; int next; }edge[maxn]; struct ppp { int v; int edge; }pre[maxn]; int s,t,ans,n,m,cnt;int root[maxn],dis[maxn],inque[maxn],vis[maxn];queue<int >q; inline void add (int x,int y,int w,int d) { edge[++cnt]={y,w,d,root[x]}; root[x]=cnt; } bool spfa () { memset (dis,0x3f ,sizeof (dis)); dis[s]=0 ; q.push (s); while (!q.empty ()){ int u=q.front (); q.pop (); inque[u]=0 ; for (int i=root[u];i;i=edge[i].next){ int v=edge[i].to; if (!edge[i].w)continue ; if (dis[v]>dis[u]+edge[i].d){ dis[v]=dis[u]+edge[i].d; pre[v].v=u; pre[v].edge=i; if (!inque[v]){ inque[v]=1 ; q.push (v); } } } } return dis[t]!=inf; } void EK () { int ans=0 ,w=0 ; while (spfa ()){ int k=inf; int f=0 ; for (int i=t;i!=s;i=pre[i].v){ k=min (edge[pre[i].edge].w,k); f+=edge[pre[i].edge].d; } for (int i=t;i!=s;i=pre[i].v){ edge[pre[i].edge].w-=k; edge[pre[i].edge^1 ].w+=k; } ans+=k; w+=k*f; } cout<<ans<<' ' <<w<<endl; } void sync () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); } signed main () { sync (); cnt=1 ; cin>>n>>m>>s>>t; while (m--){ int u,v,w,c; cin>>u>>v>>w>>c; add (u,v,w,c); add (v,u,0 ,-c); } EK (); return 0 ; }