网络流扩展知识
最小费用最大流
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 1393
code
#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
#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)
const int maxn = 1020;
const int maxm = 3e4+510;
const int inf = 0x3f3f3f3f;
int n,m,dep[maxn],head[maxn],s,t,cnt;
struct edge{
int u,v,cap,nxt;
}es[maxm<<1];
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);
}
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();
scanf("%d %d",&n,&m); cnt = 0;
CL(head,-1);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d %d",&u,&v);
addFlow(u,v+n,1);
}
for(int i=1;i<=n;i++){
addFlow(0,i,1);
addFlow(i+n,2*n+1,1);
}
int ans = 0;
while(bfs(0,2*n+1)){
ans += dinic(0,2*n+1,inf);
//db(ans);
}
printf("%d
",n-ans);
}
转换成最小割问题
- 最大权闭合子图(s->正权点,负权点->t)
- 最小点权值覆盖集
- 最大点权独立集