网络流扩展知识
最小费用最大流
luogu P3381 【模板】最小费用最大流
解析:
- 先用spfa求出最短路径(单位流量费用最少)
- 多路增广流掉这些流量(spfa没有记录dep信息,但凭借dis[]信息的关系不能保证不会访问之前访问过的节点,所以需要vis[]标记)
code
#include<bits/stdc++.h> #include<iostream> using namespace std; #define CL(a,b) memset(a,b,sizeof(a)) #define db(x) cout<<"["<<#x<<"]="<<x<<endl #define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0) const int inf = 0x3f3f3f3f; const int maxn = 5e3+100; const int maxm = 5e4+100; struct edge{ int u,v,cap,cost,nxt; }es[maxm*100]; int cnt, head[maxn],dis[maxn],vis[maxn]; void addEdge(int u,int v,int cap,int cost){ es[cnt].u = u, es[cnt].v = v,es[cnt].cap = cap,es[cnt].cost=cost; es[cnt].nxt = head[u], head[u] = cnt, cnt++; } void addFlow(int u,int v,int cap,int cost){ addEdge(u,v,cap,cost); addEdge(v,u,0,-cost); } int spfa(int s,int t){ /* spfa: 利用队列中元素对最短路径进行更新,将能够更新且不在队列中的点加入队列 */ CL(dis,inf); CL(vis,0); queue<int> q; dis[s] = 0, vis[s] = 1; q.push(s); while(!q.empty()){ int tmp = q.front(); q.pop(); vis[tmp] = 0; for(int k = head[tmp];k!=-1;k = es[k].nxt){ int u= es[k].u, v = es[k].v, cap = es[k].cap, cost = es[k].cost; if(dis[v]>dis[tmp]+cost&&cap>0){//cap>0表示联通,dis记录spfa的距离 dis[v] = dis[tmp]+cost; if(!vis[v]) {vis[v] = 1; q.push(v);} } } } return dis[t]<inf; } int dinic(int s,int t,int fl,int &mincost){//mincost记录最小费用 //本质是dfs,采用类dinic算法应该注意: // spfa没有提供层次信息,dis[v]==dis[u]+cost只意味着他们有着最短路径更新的关系,并不能保证不出现环(0) //图中k可能出现0环,所以需要vis[]防止访问了之前访问过的节点 if(s==t||fl<=0) return fl; int res = 0; vis[s] = 1; for(int k = head[s];k!=-1;k = es[k].nxt){ int u = es[k].u, v = es[k].v, cap = es[k].cap, cost = es[k].cost; if(!vis[v]&&cap>0&&dis[v] == dis[u]+cost){ //类似dinic多路增广 int f = dinic(v,t,min(fl,cap),mincost); res+=f, es[k].cap-=f, es[k^1].cap+=f, fl-=f; mincost+=(f*cost); //db(res); } } vis[s] = 0; return res; } int n,m,s,t; int u,v,cap,cost; int main(){ fast(); scanf("%d %d %d %d",&n,&m,&s,&t); //cin>>n>>m>>s>>t; cnt = 0; CL(head,-1); for(int i=0;i<m;i++){ scanf("%d %d %d %d",&u,&v,&cap,&cost); //cin>>u>>v>>cap>>cost; addFlow(u,v,cap,cost); } int ans = 0; int mincost = 0; //db("build graph"); while(spfa(s,t)){ ans+=dinic(s,t,inf,mincost); //db(ans); //db(mincost); } cout<<ans<<" "<<mincost<<endl; }
二分图的多重匹配
hiho 1393code
#include<bits/stdc++.h> using namespace std; #define CL(a,b) memset(a,b,sizeof(a)) #define db(x) cout<<"["<<#x<<"]="<<x<<endl #define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0) int t,n,m,cnt,need; const int maxn = 220; const int maxm = 110*110*2; const int inf = 0x3f3f3f3f; int head[maxn],dep[maxn],num[maxn]; struct edge{ int u,v,cap,nxt; }es[maxm]; void addEdge(int u,int v, int cap){ es[cnt].u = u, es[cnt].v= v, es[cnt].cap = cap; es[cnt].nxt = head[u], head[u] = cnt, cnt++; } void addFlow(int u,int v,int cap){ addEdge(u,v,cap);addEdge(v,u,0); } void build_graph(){ CL(head,-1); cnt = 0;need = 0; cin>>n>>m; int t,a,b; for(int i=1;i<=m;i++){cin>>t; need+=t;addFlow(n+i,n+m+1,t);} for(int i=1;i<=n;i++){ cin>>a>>b; addFlow(0,i,a); for(int j=1;j<=b;j++){ cin>>t; addFlow(i,t+n,1); } } } int bfs(int s,int t){ CL(dep,-1); dep[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int tmp = q.front(); q.pop(); for(int k = head[tmp];k!=-1;k = es[k].nxt){ int u = es[k].u, v = es[k].v, cap = es[k].cap; if(cap>0&&dep[v]<0){ dep[v] = dep[u]+1; q.push(v); } } } return dep[t]>-1; } int dinic(int s,int t,int fl){ if(s==t||fl<=0) return fl; int res = 0; for(int k = head[s];k!=-1;k=es[k].nxt){ int u = es[k].u, v = es[k].v, cap =es[k].cap; if(cap>0&&dep[v]==dep[u]+1){ int f = dinic(v,t,min(fl,cap)); fl-=f, res+=f, es[k].cap-=f, es[k^1].cap+=f; } } return res; } int main(){ fast(); cin>>t; for(int i=1;i<=t;i++){ build_graph(); int ans = 0; while(bfs(0,n+m+1)){ ans+=dinic(0,n+m+1,inf); } if(ans == need) cout<<"Yes"<<endl; else cout<<"No"<<endl; //cout<<ans<<endl; } }
最小路径覆盖
解析:
利用前驱,后继建二部图,转换成对打匹配问题;后继没有被匹配上的点代表是某条路径的起点
code
```cpp #include转换成最小割问题
- 最大权闭合子图(s->正权点,负权点->t)
- 最小点权值覆盖集
- 最大点权独立集