最大流
算法
模板题
EK算法
在每个网络中,找一条增广路径,然后更新这条增广路上边的容量,形成新的残差网络,慢慢求得最大流。
#include<bits/stdc++.h>
using namespace std;
const int N=1000,M=1e4+10,inf=0x7fffffff;
typedef long long ll;
int h[N],e[M],f[M],ne[M],idx;
int q[N],d[N],pre[N];//d表示起点到点i路径上所有边容量最小值,pre表示点i的前驱边
bool st[N];
int n,m,a,b,c,S,T;
void add(int a,int b,int c){
e[idx]=b,ne[idx]=h[a],f[idx]=c,h[a]=idx++;
e[idx]=a,ne[idx]=h[b],f[idx]=0,h[b]=idx++;
}
bool bfs(){
// memset(d,0,sizeof d);//不能有这个,d表示的是起点到每个点最小容量,即最大能通过的流量
memset(st,0,sizeof st);
int hh=0,tt=0;
q[0]=S,d[S]=inf,st[S]=true;
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];~i;i=ne[i]){
int ver=e[i];
if(!st[ver]&&f[i]){//这下面的更新看初始化有哪些
st[ver]=true;
d[ver]=min(d[t],f[i]);
pre[ver]=i;
q[++tt]=ver;
if(ver==T) return true;
}
}
}
return false;
}
ll EK(){
ll r=0;
while(bfs()){
r+=d[T];
for(int i=T;i!=S;i=e[pre[i]^1]){
f[pre[i]]-=d[T],f[pre[i]^1]+=d[T];
}
}
return r;
}
int main(){
memset(h,-1,sizeof h);//没有输出死循环就是没有初始化链式向前星
scanf("%d%d%d%d",&n,&m,&S,&T);
while(m--){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
printf("%lld\n",EK());
return 0;
}
dinic算法
对于每个网络,求所有的增广路径(dfs)。优化
对于每条增广路径的流量是小于边的容量的,但是最大流是所有增广路径流量总和,就可能比几条边的容量更大。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+10,M=2e5+210,inf=0x7fffffff;
int h[N],e[M],f[M],ne[M],idx;//f表示边的容量,最后其表示的是边的剩余容量,其反向边表示的是流量
int q[N],d[N],cur[N];//d是分层图,cur是弧优化
int n,m,S,T;//源点和汇点不要用小写,太容易和局部变量冲突错误
void add(int a,int b,int c){
e[idx]=b,ne[idx]=h[a],f[idx]=c,h[a]=idx++;
e[idx]=a,ne[idx]=h[b],f[idx]=0,h[b]=idx++;
}
bool bfs(){
int hh=0,tt=0;
memset(d,-1,sizeof d);
q[0]=S,d[S]=0,cur[S]=h[S];
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];~i;i=ne[i]){
int ver=e[i];
if(d[ver]==-1&&f[i]){
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T) return true;
q[++tt]=ver;
}
}
}
return false;
}
int find(int u,int limit){
if(u==T) return limit;
int flow=0;
for(int i=cur[u];~i&&flow<limit;i=ne[i]){//剩余流量优化,不能是小于等于,这个点流出去的流量要小于limit流量限制
cur[u]=i;//当前弧优化
int ver=e[i];
if(d[ver]==d[u]+1&&f[i]){
int t=find(ver,min(f[i],limit-flow));
if(!t) d[ver]=-1;//残枝优化
f[i]-=t,f[i^1]+=t,flow+=t;
}
}
return flow;
}
ll dinic(){
//这个注意初始化为ll类型,因为最大流可能超过int类型
ll r=0;
int flow;
while(bfs()) while(flow=find(S,inf)) r+=flow;
//一个图将其所有增广路径都找出来,然后相加。
//增广路径上的流量小于int类型而已,但是最大流是所有增广路径流量总和。
return r;
}
int main(){
scanf("%d%d%d%d",&n,&m,&S,&T);
memset(h,-1,sizeof h);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
printf("%lld\n",dinic());
return 0;
}
二分图匹配
飞行员的配对问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 5210, INF = 1e8;
int m, n, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &m, &n);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i ++ ) add(S, i, 1);
for (int i = m + 1; i <= n; i ++ ) add(i, T, 1);
int a, b;
while (cin >> a >> b, a != -1) add(a, b, 1);
printf("%d\n", dinic());
for (int i = 0; i < idx; i += 2)
if (e[i] > m && e[i] <= n && !f[i])
printf("%d %d\n", e[i ^ 1], e[i]);
return 0;
}
圆桌问题
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 430, M = (150 * 270 + N) * 2, INF = 1e8;
typedef long long ll;
int m, n, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
ll dinic()
{
ll r = 0;
int flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &m, &n);
S = 0, T = m + n + 1;
memset(h, -1, sizeof h);
int tot = 0;
for (int i = 1; i <= m; i ++ )
{
int c;
scanf("%d", &c);
add(S, i, c);
tot += c;
}
for (int i = 1; i <= n; i ++ )
{
int c;
scanf("%d", &c);
add(m + i, T, c);
}
for (int i = 1; i <= m; i ++ )
for (int j = 1; j <= n; j ++ )
add(i, m + j, 1);
if (dinic() != tot) puts("0");
else
{
puts("1");
for (int i = 1; i <= m; i ++ )
{
for (int j = h[i]; ~j; j = ne[j])
if (e[j] > m && e[j] <= m + n && !f[j])
printf("%d ", e[j] - m);
puts("");
}
}
return 0;
}
上下界可行流
无源汇上下界可行流
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 210, M = (10200 + N) * 4, INF = 1e8;
typedef long long ll;
int n, m, S, T;
int h[N], e[M], f[M], l[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = d - c, l[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
signed find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
ll dinic()
{
ll r = 0;
int flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
int q;cin>>q;
while(q--){
scanf("%d%d", &n, &m);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
memset(A,0,sizeof A);
idx=0;
for (int i = 0; i < m; i ++ )
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(a, b, c, d);
A[a] -= c, A[b] += c;
}
int tot = 0;
for (int i = 1; i <= n; i ++ )
if (A[i] > 0) add(S, i, 0, A[i]), tot += A[i];
else if (A[i] < 0) add(i, T, 0, -A[i]);
if (dinic() != tot) puts("NO");
else
{
puts("YES");
for (int i = 0; i < m * 2; i += 2)
printf("%d\n", f[i ^ 1] + l[i]);
}
puts("");
}
return 0;
}
有源汇上下界最大流
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 210, M = (N + 10000) * 2, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
int s, t;
scanf("%d%d%d%d", &n, &m, &s, &t);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(a, b, d - c);
A[a] -= c, A[b] += c;
}
int tot = 0;
for (int i = 1; i <= n; i ++ )
if (A[i] > 0) add(S, i, A[i]), tot += A[i];
else if (A[i] < 0) add(i, T, -A[i]);
add(t, s, INF);
if (dinic() < tot) puts("please go home to sleep");
else
{
int res = f[idx - 1];
S = s, T = t;
f[idx - 1] = f[idx - 2] = 0;
printf("%d\n", res + dinic());
}
return 0;
}
例题——东方文花帖
思路:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e4+10,M=4e5+10,INF=1e9+7;
ll h[N],e[M],ne[M],w[M],A[N],d[N],cur[N],idx,n,m,S,T,q[N],s,t;
void add(ll a,ll b,ll c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
e[idx]=a,w[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
//加每一个有上下界的边
void addedge(ll from,ll to,ll least,ll most)
{
add(from,to,most-least);
A[from]-=least,A[to]+=least;
}
bool bfs()
{
memset(d,0,sizeof d);
ll hh=1,tt=1;
q[tt]=S;d[S]=1;cur[S]=h[S];
while (hh<=tt)
{
ll x=q[hh++];
for (ll i=h[x];i!=-1;i=ne[i])
if (!d[e[i]]&&w[i])
{
d[e[i]]=d[x]+1;
cur[e[i]]=h[e[i]];
if (e[i]==T) return true;
q[++tt]=e[i];
}
}
return false;
}
inline ll read()
{
char c;
ll res=0,f=1;
while ((c=getchar())<'0'||c>'9')
if (c=='-') f=-1;
res=c-'0';
while ((c=getchar())<='9'&&c>='0') res=(res<<3)+(res<<1)+c-'0';
return res*f;
}
ll findflow(ll x,ll limit)
{
ll flow=0;
if (x==T) return limit;
for (ll i=cur[x];i!=-1&&flow<limit;i=ne[i])
{
cur[x]=i;//当前弧优化
if ((d[e[i]]==d[x]+1)&&w[i])
{
ll t=findflow(e[i],min(limit-flow,w[i]));
if (!t) d[e[i]]=-1000;
w[i]-=t,w[i^1]+=t,flow+=t;
}
}
return flow;
}
//dinic 模板
ll dinic()
{
ll r=0,flow;
while (bfs())
while (flow=findflow(S,INF)) r+=flow;
return r;
}
int main()
{
while (~scanf("%d%d",&n,&m))//敲黑板
{
memset(h,-1,sizeof h);
memset(A,0,sizeof A);//A 是指多出来的流量
s=0,t=m+n+1;ll tot=0;idx=0;//原图的源汇点
for (ll i=1,x;i<=m;++i)
{
x=read();
addedge(s,i,x,INF);
}
for (ll i=1,C,D;i<=n;++i)
{
C=read();D=read();
addedge(m+i,t,0,D);
for (ll j=1,x,L,R;j<=C;++j)
{
x=read(),L=read(),R=read();
addedge(x+1,m+i,L,R);
}
}
S=m+n+2;T=m+n+3;//新图的源汇点
for (ll i=0;i<=m+n+1;++i)
{
if (A[i]>0) add(S,i,A[i]),tot+=A[i];
else if (A[i]<0) add(i,T,-A[i]);
}
add(t,s,INF);
if (dinic()<tot)//判断是否为满流
{
puts("-1\n");
continue;
}
ll res=w[idx-1];
w[idx-1]=w[idx-2]=0;
S=s,T=t;
printf("%d\n\n",res+dinic());
}
return 0;
}
有源汇上下界最小流
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50010, M = (N + 125003) * 2, INF = 2147483647;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N], A[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
int s, t;
scanf("%d%d%d%d", &n, &m, &s, &t);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(a, b, d - c);
A[a] -= c, A[b] += c;
}
int tot = 0;
for (int i = 1; i <= n; i ++ )
if (A[i] > 0) add(S, i, A[i]), tot += A[i];
else if (A[i] < 0) add(i, T, -A[i]);
add(t, s, INF);
if (dinic() < tot) puts("please go home to sleep");
else
{
int res = f[idx - 1];
S = t, T = s;
f[idx - 1] = f[idx - 2] = 0;
printf("%d\n", res - dinic());
}
return 0;
}
多源汇最大流
多源汇最大流(最大流,超级源汇点)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = (100000 + N) * 2, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
int sc, tc;
scanf("%d%d%d%d", &n, &m, &sc, &tc);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
while (sc -- )
{
int x;
scanf("%d", &x);
add(S, x, INF);
}
while (tc -- )
{
int x;
scanf("%d", &x);
add(x, T, INF);
}
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
关键边
伊基的故事 I - 道路重建
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510, M = 10010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
bool vis_s[N], vis_t[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
void dfs(int u, bool st[], int t)
{
st[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int j = i ^ t, ver = e[i];
if (f[j] && !st[ver])
dfs(ver, st, t);
}
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n - 1;
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
dinic();
dfs(S, vis_s, 0);
dfs(T, vis_t, 1);
int res = 0;
for (int i = 0; i < m * 2; i += 2)
if (!f[i] && vis_s[e[i ^ 1]] && vis_t[e[i]])
res ++ ;
printf("%d\n", res);
return 0;
}
最大流判定
秘密挤奶机(最大流,最大流判定,二分)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 210, M = 80010, INF = 1e8;
int n, m, K, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, w[idx] = c, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
bool check(int mid)
{
for (int i = 0; i < idx; i ++ )
if (w[i] > mid) f[i] = 0;
else f[i] = 1;
return dinic() >= K;
}
int main()
{
scanf("%d%d%d", &n, &m, &K);
S = 1, T = n;
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
int l = 1, r = 1e6;
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", r);
return 0;
}
星际转移问题(最大流,最大流判定,分层图)
建图:
地球为0,月球为n+1,1~n为太空站,(i,j)表示第j天站点i上人数的状态。
1.首先第0天,k个人都在站点0,所以从源点向(0,0)连一条容量为k的边
2.所有(n+1,~)都需要向汇点连接一条容量为无穷的边,表示到达月球的人数
3.所有站点都需要从前一天向当前天连接一条容量为无穷的边(i,day-1)到(i,day)容量为无穷
4.遍历所有公交车路线,h为最大运输人数,r为一共经过多少站点,需要从路线前一天的站点向当前天站点连接一条容量为h的边。((day-1)%r,day-1)向(day%r,day)连接一条容量为h的边
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1101 * 50 + 10, M = (N + 1100 + 20 * 1101) + 10, INF = 1e8;
int n, m, k, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
struct Ship
{
int h, r, id[30];
}ships[30];
int p[30];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int get(int i, int day)
{
return day * (n + 2) + i;
}
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
S = N - 2, T = N - 1;
memset(h, -1, sizeof h);
for (int i = 0; i < 30; i ++ ) p[i] = i;
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
ships[i] = {a, b};//最大运输人数,经过站点数
for (int j = 0; j < b; j ++ )
{
int id;
scanf("%d", &id);
if (id == -1) id = n + 1;//月球编号转换
ships[i].id[j] = id;
if (j)
{
int x = ships[i].id[j - 1];
p[find(x)] = find(id);
}
}
}
if (find(0) != find(n + 1)) puts("0");
else
{
add(S, get(0, 0), k);
add(get(n + 1, 0), T, INF);
int day = 1, res = 0;
while (true)
{
add(get(n + 1, day), T, INF);
for (int i = 0; i <= n + 1; i ++ )
add(get(i, day - 1), get(i, day), INF);
for (int i = 0; i < m; i ++ )
{
int r = ships[i].r;
int a = ships[i].id[(day - 1) % r], b = ships[i].id[day % r];
add(get(a, day - 1), get(b, day), ships[i].h);
}
res += dinic();
if (res >= k) break;
day ++ ;
}
printf("%d\n", day);
}
return 0;
}
拆点(不相交路径,只能用k次)
餐饮(最大流,拆点)
建图:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 410, M = 40610, INF = 1e8;
int n, F, D, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d%d", &n, &F, &D);
S = 0, T = n * 2 + F + D + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= F; i ++ ) add(S, n * 2 + i, 1);
for (int i = 1; i <= D; i ++ ) add(n * 2 + F + i, T, 1);
for (int i = 1; i <= n; i ++ )
{
add(i, n + i, 1);
int a, b, t;
scanf("%d%d", &a, &b);
while (a -- )
{
scanf("%d", &t);
add(n * 2 + t, i, 1);
}
while (b -- )
{
scanf("%d", &t);
add(i + n, n * 2 + F + t, 1);
}
}
printf("%d\n", dinic());
return 0;
}
最长递增子序列问题(最大流,拆点,DP)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010, M = 251010, INF = 1e8;
int n, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
int g[N], w[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d", &n);
S = 0, T = n * 2 + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
int s = 0;
for (int i = 1; i <= n; i ++ )
{
add(i, i + n, 1);
g[i] = 1;
for (int j = 1; j < i; j ++ )
if (w[j] <= w[i])
g[i] = max(g[i], g[j] + 1);
for (int j = 1; j < i; j ++ )
if (w[j] <= w[i] && g[j] + 1 == g[i])
add(n + j, i, 1);
s = max(s, g[i]);
if (g[i] == 1) add(S, i, 1);
}
for (int i = 1; i <= n; i ++ )
if (g[i] == s)
add(n + i, T, 1);
printf("%d\n", s);
if (s == 1) printf("%d\n%d\n", n, n);
else
{
int res = dinic();
printf("%d\n", res);
for (int i = 0; i < idx; i += 2)
{
int a = e[i ^ 1], b = e[i];
if (a == S && b == 1) f[i] = INF;
else if (a == 1 && b == n + 1) f[i] = INF;
else if (a == n && b == n + n) f[i] = INF;
else if (a == n + n && b == T) f[i] = INF;
}
printf("%d\n", res + dinic());
}
return 0;
}
企鹅游行(最大流+拆点+枚举汇点)
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 210, M = 20410, INF = 1e8;
const double eps = 1e-8;
int n, S, T;
double D;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
PII p[N];
bool check(PII a, PII b)
{
double dx = a.x - b.x, dy = a.y - b.y;
return dx * dx + dy * dy < D * D + eps;
}
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
int cases;
scanf("%d", &cases);
while (cases -- )
{
memset(h, -1, sizeof h);
idx = 0;
scanf("%d%lf", &n, &D);
S = 0;
int tot = 0;
for (int i = 1; i <= n; i ++ )
{
int x, y, a, b;
scanf("%d%d%d%d", &x, &y, &a, &b);
p[i] = {x, y};
add(S, i, a);//源点
add(i, n + i, b);//拆点,中间容量为起跳次数
tot += a;
}
for (int i = 1; i <= n; i ++ )
for (int j = i + 1; j <= n; j ++ )
if (check(p[i], p[j]))
{
add(n + i, j, INF);
add(n + j, i, INF);
}
int cnt = 0;
for (int i = 1; i <= n; i ++ )
{
T = i;
for (int j = 0; j < idx; j += 2)//恢复原图
{
f[j] += f[j ^ 1];
f[j ^ 1] = 0;
}
if (dinic() == tot)
{
printf("%d ", i - 1);
cnt ++ ;
}
}
if (!cnt) puts("-1");
else puts("");
}
return 0;
}
建图实战
猪(最大流,分层图,困难题)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 100200 * 2 + 10, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
int w[1010], belong[1010];//belong[i]表示猪舍编号i上一个顾客打开的顾客编号
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs())
{
r += find(S, INF);
flow = find(S, INF);
if (flow) puts("!");
r += flow;
}
return r;
}
int main()
{
scanf("%d%d", &m, &n);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i ++ ) scanf("%d", &w[i]);
for (int i = 1; i <= n; i ++ )
{
int a, b;
scanf("%d", &a);
while (a -- )
{
int t;
scanf("%d", &t);
if (!belong[t]) add(S, i, w[t]);
else add(belong[t], i, INF);
belong[t] = i;
}
scanf("%d", &b);
add(i, T, b);
}
printf("%d\n", dinic());
return 0;
}
最小割
模板
Dinic/ISAP求最小割
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = 200010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
直接应用
网络战争(最小割+01分数规划+二分)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 810, INF = 1e8;
const double eps = 1e-8;
int n, m, S, T;
int h[N], e[M], w[M], ne[M], idx;
double f[M];
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, w[idx] = c, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i] > 0)
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
double find(int u, double limit)
{
if (u == T) return limit;
double flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i] > 0)
{
double t = find(ver, min(f[i], limit - flow));
if (t < eps) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
double dinic(double mid)
{
double res = 0;
for (int i = 0; i < idx; i += 2)
if (w[i] <= mid)
{
res += w[i] - mid;
f[i] = f[i ^ 1] = 0;
}
else f[i] = f[i ^ 1] = w[i] - mid;
double r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r + res;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
double l = 0, r = 1e7;
while (r - l > eps)
{
double mid = (l + r) / 2;
if (dinic(mid) < 0) r = mid;
else l = mid;
}
printf("%.2lf\n", r);
return 0;
}
最优标号(最小割,位运算)#困难,想不到
将一个点放进集合,其集合源点要向该点连一条容量为无穷的边,这样在残差网络中,这条边还是存在,即在一个集合中。(不希望某条边为割边的话,设为无穷即可)
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 510, M = (3000 + N * 2) * 2, INF = 1e8;
int n, m, k, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
int p[N];
PII edges[3010];
void add(int a, int b, int c1, int c2)
{
e[idx] = b, f[idx] = c1, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = c2, ne[idx] = h[b], h[b] = idx ++ ;
}
void build(int k)
{
memset(h, -1, sizeof h);
idx = 0;
for (int i = 0; i < m; i ++ )
{
int a = edges[i].x, b = edges[i].y;
add(a, b, 1, 1);
}
for (int i = 1; i <= n; i ++ )
if (p[i] >= 0)
{
if (p[i] >> k & 1) add(i, T, INF, 0);
else add(S, i, INF, 0);
}
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
LL dinic(int k)
{
build(k);
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n + 1;
for (int i = 0; i < m; i ++ ) scanf("%d%d", &edges[i].x, &edges[i].y);
scanf("%d", &k);
memset(p, -1, sizeof p);
while (k -- )
{
int a, b;
scanf("%d%d", &a, &b);
p[a] = b;
}
LL res = 0;
for (int i = 0; i <= 30; i ++ ) res += dinic(i) << i;
printf("%lld\n", res);
return 0;
}
最大权闭合子图(有向图)
最大获利(最大权闭合子图+最小割)
最大权闭合子图
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 55010, M = (50000 * 3 + 5000) * 2 + 10, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n + m + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
int p;
scanf("%d", &p);
add(m + i, T, p);
}
int tot = 0;
for (int i = 1; i <= m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(S, i, c);
add(i, m + a, INF);
add(i, m + b, INF);
tot += c;
}
printf("%d\n", tot - dinic());
return 0;
}
最大密度子图
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010, M = (50000 + N * 2) * 2 + 10, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
int dg[N], p[N];
void add(int a, int b, int c1, int c2)
{
e[idx] = b, f[idx] = c1, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = c2, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ ) scanf("%d", &p[i]), p[i] *= -1;
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c, c);
dg[a] += c, dg[b] += c;
}
int U = 0;
for (int i = 1; i <= n; i ++ ) U = max(U, 2 * p[i] + dg[i]);
for (int i = 1; i <= n; i ++ )
{
add(S, i, U, 0);
add(i, T, U - 2 * p[i] - dg[i], 0);
}
printf("%d\n", (U * n - dinic()) / 2);
return 0;
}
最大密度子图(无向图)
边数为3E+V,将边看成点,然后从这个点向边的两个端点连边。
生活的艰辛
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = (1000 + N * 2) * 2, INF = 1e8;
int n, m, S, T;
int h[N], e[M], ne[M], idx;
double f[M];
int q[N], d[N], cur[N];
int dg[N];
struct Edge
{
int a, b;
}edges[M];
int ans;
bool st[N];
void add(int a, int b, double c1, double c2)
{
e[idx] = b, f[idx] = c1, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = c2, ne[idx] = h[b], h[b] = idx ++ ;
}
void build(double g)
{
memset(h, -1, sizeof h);
idx = 0;
for (int i = 0; i < m; i ++ ) add(edges[i].a, edges[i].b, 1, 1);
for (int i = 1; i <= n; i ++ )
{
add(S, i, m, 0);
add(i, T, m + g * 2 - dg[i], 0);
}
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i] > 0)
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
double find(int u, double limit)
{
if (u == T) return limit;
double flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i] > 0)
{
double t = find(ver, min(f[i], limit - flow));
if (t <= 0) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
double dinic(double g)
{
build(g);
double r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
void dfs(int u)
{
st[u] = true;
if (u != S) ans ++ ;
for (int i = h[u]; ~i; i = ne[i])
{
int ver = e[i];
if (!st[ver] && f[i] > 0)
dfs(ver);
}
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n + 1;
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
dg[a] ++, dg[b] ++ ;
edges[i] = {a, b};
}
double l = 0, r = m;
while (r - l > 1e-8)
{
double mid = (l + r) / 2;
double t = dinic(mid);
if (m * n - t > 0) l = mid;
else r = mid;
}
dinic(l);
dfs(S);
if (!ans) puts("1\n1");
else
{
printf("%d\n", ans);
for (int i = 1; i <= n; i ++ )
if (st[i])
printf("%d\n", i);
}
return 0;
}
最小点权覆盖集(有向图)
有向图破坏
分析:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 210, M = 5200 * 2 + 10, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
void dfs(int u)
{
st[u] = true;
for (int i = h[u]; ~i; i = ne[i])
if (f[i] && !st[e[i]])
dfs(e[i]);
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n * 2 + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
int w;
scanf("%d", &w);
add(S, i, w);
}
for (int i = 1; i <= n; i ++ )
{
int w;
scanf("%d", &w);
add(n + i, T, w);
}
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
add(b, n + a, INF);
}
printf("%d\n", dinic());
dfs(S);
int cnt = 0;
for (int i = 0; i < idx; i += 2)
{
int a = e[i ^ 1], b = e[i];
if (st[a] && !st[b]) cnt ++ ;
}
printf("%d\n", cnt);
for (int i = 0; i < idx; i += 2)
{
int a = e[i ^ 1], b = e[i];
if (st[a] && !st[b])
{
if (a == S) printf("%d +\n", b);
}
}
for (int i = 0; i < idx; i += 2)
{
int a = e[i ^ 1], b = e[i];
if (st[a] && !st[b])
{
if (b == T) printf("%d -\n", a - n);
}
}
return 0;
}
最大点权独立集(有向图)
王者之剑
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = 60010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
int get(int x, int y)
{
return (x - 1) * m + y;
}
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n * m + 1;
memset(h, -1, sizeof h);
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int tot = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
{
int w;
scanf("%d", &w);
if (i + j & 1)
{
add(S, get(i, j), w);
for (int k = 0; k < 4; k ++ )
{
int x = i + dx[k], y = j + dy[k];
if (x >= 1 && x <= n && y >= 1 && y <= m)
add(get(i, j), get(x, y), INF);
}
}
else
add(get(i, j), T, w);
tot += w;
}
printf("%d\n", tot - dinic());
return 0;
}
建图实战
有线电视网络
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 5210, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
while (cin >> n >> m)
{
memset(h, -1, sizeof h);
idx = 0;
for (int i = 0; i < n; i ++ ) add(i, n + i, 1);
while (m -- )
{
int a, b;
scanf(" (%d,%d)", &a, &b);
add(n + a, b, INF);
add(n + b, a, INF);
}
int res = n;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < i; j ++ )
{
S = n + i, T = j;
for (int k = 0; k < idx; k += 2)
{
f[k] += f[k ^ 1];
f[k ^ 1] = 0;
}
res = min(res, dinic());
}
printf("%d\n", res);
}
return 0;
}
太空飞行问题
思路:
#include <iostream>
#include <cstring>
#include <sstream>
#include <algorithm>
using namespace std;
const int N = 110, M = 5210, INF = 1e8;
int m, n, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
void dfs(int u)
{
st[u] = true;
for (int i = h[u]; ~i; i = ne[i])
if (!st[e[i]] && f[i])
dfs(e[i]);
}
int main()
{
scanf("%d%d", &m, &n);
S = 0, T = m + n + 1;
memset(h, -1, sizeof h);
getchar(); // 过滤掉第一行最后的回程
int tot = 0;
for (int i = 1; i <= m; i ++ )
{
int w, id;
string line;
getline(cin, line);
stringstream ssin(line);
ssin >> w;
add(S, i, w);
while (ssin >> id) add(i, m + id, INF);
tot += w;
}
for (int i = 1; i <= n; i ++ )
{
int p;
cin >> p;
add(m + i, T, p);
}
int res = dinic();
dfs(S);
for (int i = 1; i <= m; i ++ )
if (st[i]) printf("%d ", i);
puts("");
for (int i = m + 1; i <= m + n; i ++ )
if (st[i]) printf("%d ", i - m);
puts("");
printf("%d\n", tot - res);
return 0;
}
骑士共存问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 40010, M = 400010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
bool g[210][210];
int get(int x, int y)
{
return (x - 1) * n + y;
}
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n * n + 1;
memset(h, -1, sizeof h);
while (m -- )
{
int x, y;
scanf("%d%d", &x, &y);
g[x][y] = true;
}
int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[] = {1, 2, 2, 1, -1, -2, -2, -1};
int tot = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
if (g[i][j]) continue;
if (i + j & 1)
{
add(S, get(i, j), 1);
for (int k = 0; k < 8; k ++ )
{
int x = i + dx[k], y = j + dy[k];
if (x >= 1 && x <= n && y >= 1 && y <= n && !g[x][y])
add(get(i, j), get(x, y), INF);
}
}
else add(get(i, j), T, 1);
tot ++ ;
}
printf("%d\n", tot - dinic());
return 0;
}
费用流
模板
费用流(EK算法)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010, M = 100010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
void EK(int& flow, int& cost)
{
flow = cost = 0;
while (spfa())
{
int t = incf[T];
flow += t, cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(a, b, c, d);
}
int flow, cost;
EK(flow, cost);
printf("%d %d\n", flow, cost);
return 0;
}
直接应用
运输问题
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 160, M = 5150 * 2 + 10, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(incf[t], f[i]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d%d", &m, &n);
S = 0, T = m + n + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i ++ )
{
int a;
scanf("%d", &a);
add(S, i, a, 0);
}
for (int i = 1; i <= n; i ++ )
{
int b;
scanf("%d", &b);
add(m + i, T, b, 0);
}
for (int i = 1; i <= m; i ++ )
for (int j = 1; j <= n; j ++ )
{
int c;
scanf("%d", &c);
add(i, m + j, INF, c);
}
printf("%d\n", EK());
for (int i = 0; i < idx; i += 2)
{
f[i] += f[i ^ 1], f[i ^ 1] = 0;
w[i] = -w[i], w[i ^ 1] = -w[i ^ 1];
}
printf("%d\n", -EK());
return 0;
}
负载平衡
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 610, INF = 1e8;
int n, S, T;
int s[N];
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d", &n);
S = 0, T = n + 1;
memset(h, -1, sizeof h);
int tot = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &s[i]);
tot += s[i];
add(i, i < n ? i + 1 : 1, INF, 1);
add(i, i > 1 ? i - 1 : n, INF, 1);
}
tot /= n;
for (int i = 1; i <= n; i ++ )
if (tot < s[i])
add(S, i, s[i] - tot, 0);
else if (tot > s[i])
add(i, T, tot - s[i], 0);
printf("%d\n", EK());
return 0;
}
二分图最优匹配
分配问题
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 5210, INF = 1e8;
int n, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d", &n);
S = 0, T = n * 2 + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
add(S, i, 1, 0);
add(n + i, T, 1, 0);
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
int c;
scanf("%d", &c);
add(i, n + j, 1, c);
}
printf("%d\n", EK());
for (int i = 0; i < idx; i += 2)
{
f[i] += f[i ^ 1], f[i ^ 1] = 0;
w[i] = -w[i], w[i ^ 1] = -w[i ^ 1];
}
printf("%d\n", -EK());
return 0;
}
最大权不相交路径
数字梯形问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1200, M = 4000, INF = 1e8;
int m, n, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
int id[40][40], cost[40][40];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, -0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] < d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
int cnt = 0;
scanf("%d%d", &m, &n);
S = ++ cnt;
T = ++ cnt;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m + i - 1; j ++ )
{
scanf("%d", &cost[i][j]);
id[i][j] = ++ cnt;
}
// 规则1
memset(h, -1, sizeof h), idx = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m + i - 1; j ++ )
{
add(id[i][j] * 2, id[i][j] * 2 + 1, 1, cost[i][j]);
if (i == 1) add(S, id[i][j] * 2, 1, 0);
if (i == n) add(id[i][j] * 2 + 1, T, 1, 0);
if (i < n)
{
add(id[i][j] * 2 + 1, id[i + 1][j] * 2, 1, 0);
add(id[i][j] * 2 + 1, id[i + 1][j + 1] * 2, 1, 0);
}
}
printf("%d\n", EK());
// 规则2
memset(h, -1, sizeof h), idx = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m + i - 1; j ++ )
{
add(id[i][j] * 2, id[i][j] * 2 + 1, INF, cost[i][j]);
if (i == 1) add(S, id[i][j] * 2, 1, 0);
if (i == n) add(id[i][j] * 2 + 1, T, INF, 0);
if (i < n)
{
add(id[i][j] * 2 + 1, id[i + 1][j] * 2, 1, 0);
add(id[i][j] * 2 + 1, id[i + 1][j + 1] * 2, 1, 0);
}
}
printf("%d\n", EK());
// 规则3
memset(h, -1, sizeof h), idx = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m + i - 1; j ++ )
{
add(id[i][j] * 2, id[i][j] * 2 + 1, INF, cost[i][j]);
if (i == 1) add(S, id[i][j] * 2, 1, 0);
if (i == n) add(id[i][j] * 2 + 1, T, INF, 0);
if (i < n)
{
add(id[i][j] * 2 + 1, id[i + 1][j] * 2, INF, 0);
add(id[i][j] * 2 + 1, id[i + 1][j + 1] * 2, INF, 0);
}
}
printf("%d\n", EK());
return 0;
}
网格图模型
K取方格数
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010, M = 20010, INF = 1e8;
int n, k, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
int get(int x, int y, int t)
{
return (x * n + y) * 2 + t;
}
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, -0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] < d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(incf[t], f[i]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d%d", &n, &k);
S = 2 * n * n, T = S + 1;
memset(h, -1, sizeof h);
add(S, get(0, 0, 0), k, 0);
add(get(n - 1, n - 1, 1), T, k, 0);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
{
int c;
scanf("%d", &c);
add(get(i, j, 0), get(i, j, 1), 1, c);
add(get(i, j, 0), get(i, j, 1), INF, 0);
if (i + 1 < n) add(get(i, j, 1), get(i + 1, j, 0), INF, 0);
if (j + 1 < n) add(get(i, j, 1), get(i, j + 1, 0), INF, 0);
}
printf("%d\n", EK());
return 0;
}
深海机器人问题
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 260, M = 2000, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
int get(int x, int y)
{
return x * (m + 1) + y;
}
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, -0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] < d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
int A, B;
scanf("%d%d%d%d", &A, &B, &n, &m);
S = (n + 1) * (m + 1), T = S + 1;
memset(h, -1, sizeof h);
for (int i = 0; i <= n; i ++ )
for (int j = 0; j < m; j ++ )
{
int c;
scanf("%d", &c);
add(get(i, j), get(i, j + 1), 1, c);
add(get(i, j), get(i, j + 1), INF, 0);
}
for (int i = 0; i <= m; i ++ )
for (int j = 0; j < n; j ++ )
{
int c;
scanf("%d", &c);
add(get(j, i), get(j + 1, i), 1, c);
add(get(j, i), get(j + 1, i), INF, 0);
}
while (A -- )
{
int k, x, y;
scanf("%d%d%d", &k, &x, &y);
add(S, get(x, y), k, 0);
}
while (B -- )
{
int r, x, y;
scanf("%d%d%d", &r, &x, &y);
add(get(x, y), T, r, 0);
}
printf("%d\n", EK());
return 0;
}
拆点
餐巾计划问题
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1610, M = 10000, INF = 1e8;
int n, p, x, xp, y, yp, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d%d%d%d%d%d", &n, &p, &x, &xp, &y, &yp);
S = 0, T = n * 2 + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
int r;
scanf("%d", &r);
add(S, i, r, 0);
add(n + i, T, r, 0);
add(S, n + i, INF, p);
if (i < n) add(i, i + 1, INF, 0);
if (i + x <= n) add(i, n + i + x, INF, xp);
if (i + y <= n) add(i, n + i + y, INF, yp);
}
printf("%d\n", EK());
return 0;
}
上下界可行流
志愿者招募
思路:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010, M = 24010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
void add(int a, int b, int c, int d)
{
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}
bool spfa()
{
int hh = 0, tt = 1;
memset(d, 0x3f, sizeof d);
memset(incf, 0, sizeof incf);
q[0] = S, d[S] = 0, incf[S] = INF;
while (hh != tt)
{
int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (f[i] && d[ver] > d[t] + w[i])
{
d[ver] = d[t] + w[i];
pre[ver] = i;
incf[ver] = min(f[i], incf[t]);
if (!st[ver])
{
q[tt ++ ] = ver;
if (tt == N) tt = 0;
st[ver] = true;
}
}
}
}
return incf[T] > 0;
}
int EK()
{
int cost = 0;
while (spfa())
{
int t = incf[T];
cost += t * d[T];
for (int i = T; i != S; i = e[pre[i] ^ 1])
{
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
return cost;
}
int main()
{
scanf("%d%d", &n, &m);
S = 0, T = n + 2;
memset(h, -1, sizeof h);
int last = 0;
for (int i = 1; i <= n; i ++ )
{
int cur;
scanf("%d", &cur);
if (last > cur) add(S, i, last - cur, 0);
else if (last < cur) add(i, T, cur - last, 0);
add(i, i + 1, INF - cur, 0);
last = cur;
}
add(S, n + 1, last, 0);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(b + 1, a, INF, c);
}
printf("%d\n", EK());
return 0;
}