图论——网络流问题

最大流

算法

模板题

P3376
在这里插入图片描述

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;
}
  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值