【图论】网络流 Dinic最大流

Dinic求最大流 

AcWing 2172. Dinic/ISAP求最大流 - AcWing

#include<iostream>
using namespace std;
#define INF 0x3f3f3f3f
#include<cstring>
const int M=100010;
const int N=10010;
int n,m,S,T;
struct EDGE{
    int next;
    int to;
    int f;
}edge[2*M];
int d[N],cur[N],head[N];
int tot=0;
void add(int u,int v,int c){
    edge[tot].next=head[u];
    edge[tot].to=v;
    edge[tot].f=c;
    head[u]=tot++;

    edge[tot].next=head[v];
    edge[tot].to=u;
    edge[tot].f=0;
    head[v]=tot++;
}

bool bfs(){
     memset(d,-1,sizeof(d));
    int q[N];
    int hh=0;int tt=0;
    q[0]=S;d[S]=0;cur[S]=head[S];

    while(hh<=tt){
        int t=q[hh++];

        for(int i=head[t];~i;i=edge[i].next){
            int v=edge[i].to;
            if(d[v]==-1&&edge[i].f){

                d[v]=d[t]+1;
                cur[v]=head[v];
                if(v==T)return true;
                q[++tt]=v;
            }
        }
    }

    return false;
}

int findd(int u,int limit){
    if(u==T)return limit;
    int r=0;
    for(int i=cur[u];~i&&r<limit;i=edge[i].next){
        int v=edge[i].to;
        if(d[v]==d[u]+1&&edge[i].f){
            cur[u]=i;
            int t=findd(v,min(edge[i].f,limit-r));
            if(!t)d[v]=-1;
            r+=t;
            edge[i].f-=t;edge[i^1].f+=t;
        }
    }

    return r;
}

int  dinic(){
    int r=0;int flow;
    while(bfs()){

        while(flow=findd(S,INF)){


            r+=flow;
        }

    }
    return r;
}

int main(){
    cin>>n>>m>>S>>T;

    memset(head,-1,sizeof(head));
    while(m--)
{
    int a,b,c;
    cin>>a>>b>>c;
    add(a,b,c);
}
cout<<dinic();
}

1.解决二分图匹配问题 

源点到匹配左加一条边,容量为匹配左容量

匹配右到汇点加一条边,容量为匹配右容量

输出方案看中间榨干

证明:任何一个可行解对应一个可行流,在中间勾边,流量守恒,容量限制

任何一个可行流对应一个可行解 ,每个匹配左最多匹配一个右,每个匹配右最多匹配一个左

,每个左最多派多少个,每个右最多派多少个

飞行员配对 

AcWing 2175. 飞行员配对方案问题 - AcWing

第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。

由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的 22 名飞行员,其中 11 名是英国飞行员,另 11 名是外籍飞行员。

在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。

如何选择配对飞行的飞行员才能使一次派出最多的飞机。

对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。

输入格式

第 11 行有 22 个正整数 mm 和 nn。mm 是外籍飞行员数;nn 是皇家空军的飞行员总数。

外籍飞行员编号为 1∼m1∼m;英国飞行员编号为 m+1∼nm+1∼n。

接下来每行有 22 个正整数 ii 和 jj,表示外籍飞行员 ii 可以和英国飞行员 jj 配合。

文件最后以 22 个 −1−1 结束。

#include<iostream>
using namespace std;
#include<cstring>
#define INF 0x3f3f3f3f
const int M=100*2+100*2+100*100*2+10;
const int N=210;
int d[N];int head[N];int cur[N];
int m,n,S,T;
struct EDGE{
    int next;
    int to;
    int f;
}edge[M];
int tot=0;
void add(int u,int v,int c){
    edge[tot].next=head[u];
    edge[tot].to=v;
    edge[tot].f=c;
    head[u]=tot++;
    
    edge[tot].next=head[v];
    edge[tot].to=u;
    edge[tot].f=0;
    head[v]=tot++;
}
bool bfs(){
    int q[N];
    memset(d,-1,sizeof(d));
    int hh=0;int tt=0;
    q[0]=S;
    d[S]=0;cur[S]=head[S];
    
    while(hh<=tt){
        int t=q[hh++];
        for(int i=head[t];~i;i=edge[i].next){
            int v=edge[i].to;
            if(d[v]==-1&&edge[i].f){
                d[v]=d[t]+1;
                cur[v]=head[v];
                if(v==T)return true;
                q[++tt]=v;
            }
        }
    }
    return false;
}
int findd(int u,int limit){
    if(u==T)return limit;
    int r=0;
    
    for(int i=head[u];~i&&r<limit;i=edge[i].next){
        int v=edge[i].to;
        if(d[v]==d[u]+1&&edge[i].f){
            cur[u]=i;
            int t=findd(v,min(edge[i].f,limit-r));
            if(!t)d[v]=-1;
            edge[i].f-=t;edge[i^1].f+=t;
            r+=t;
        }
    }
    return r;
}

int dinic(){
    int r=0;int flow;
    while(bfs()){
        while(flow=findd(S,INF)){
            r+=flow;
        }
    }
    return r;
}

int main(){
    cin>>m>>n;
    S=0;T=n+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++){
        add(S,i,1);
    }
   while(1){
       int a,b;cin>>a>>b;
       if(a==-1&&b==-1)break;
       add(a,b,1);
   }
    for(int i=m+1;i<=n;i++){
        add(i,T,1);
    }
    cout<<dinic()<<endl;
    
    for(int i=0;i<=tot;i+=2){
        if(edge[i].to>=m+1&&edge[i].to<=n){
            if(!edge[i].f){
                cout<<edge[i^1].to<<" "<<edge[i].to<<endl;
            }
        }
    }
}

圆桌问题

2179. 圆桌问题 - AcWing题库 

假设有来自 mm 个不同单位的代表参加一次国际会议。

每个单位的代表数分别为 ri(i=1,2,…,m)ri(i=1,2,…,m)。

会议餐厅共有 nn 张餐桌,每张餐桌可容纳 ci(i=1,2,…,n)ci(i=1,2,…,n) 个代表就餐。

为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。

试设计一个算法,给出满足要求的代表就餐方案。

输入格式

第 11 行有 22 个正整数 mm 和 nn,mm 表示单位数,nn 表示餐桌数。

第 22 行有 mm 个正整数,分别表示每个单位的代表数 riri。

第 33 行有 nn 个正整数,分别表示每个餐桌的容量 cici。

输出格式

如果问题有解,在第 11 行输出 11,否则输出 00。

接下来的 mm 行给出每个单位代表的就餐桌号。

如果有多个满足要求的方案,只要求输出 11 个方案。

 

#include<iostream>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
int n,m,S,T;
const int M=(150+270+150*270+10)*2;
const int N=150+270+10;
struct EDGE{
    int next;
    int to;
    int f;
}edge[M];
int head[N],d[N],cur[N];int tot=0;
void add(int u,int v,int f){
    edge[tot].next=head[u];
    edge[tot].to=v;
    edge[tot].f=f;
    head[u]=tot++;

    edge[tot].next=head[v];
    edge[tot].to=u;
    edge[tot].f=0;
    head[v]=tot++;
}
bool bfs(){
    //int cnt1=0;
    memset(d,-1,sizeof(d));
    int q[N];
    int hh=0;int tt=0;
    q[0]=S;
    d[S]=0;cur[S]=head[S];

    while(hh<=tt){

        int u=q[hh++];
        //if(cnt1<=10)cout<<u<<endl,cnt1++;
        for(int i=head[u];~i;i=edge[i].next){
            int v=edge[i].to;
            if(d[v]==-1&&edge[i].f){
                d[v]=d[u]+1;
                cur[v]=head[v];
                if(v==T)return true;
                q[++tt]=v;
            }
        }
    }
    return false;
}

int findd(int u,int limit){
    if(u==T)return limit;
    int r=0;

    for(int i=cur[u];~i&&r<limit;i=edge[i].next){
        int v=edge[i].to;
        if(d[v]==d[u]+1&&edge[i].f){
            cur[u]=i;
            int t=findd(v,min(edge[i].f,limit-r));
            if(!t)d[v]=-1;
            r+=t;
            edge[i].f-=t;edge[i^1].f+=t;

        }
    }
    return r;
}

int dinic(){
    int r=0,flow;
    while(bfs()){
            //cout<<"ok"<<endl;
        while(flow=findd(S,INF))r+=flow;
    }
    return r;

}

int main(){
    cin>>m>>n;
    S=0;T=m+n+1;
    memset(head,-1,sizeof(head));
    int sum=0;

    for(int i=1;i<=m;i++)
{
    int c;cin>>c;
    sum+=c;
    add(S,i,c);
}
for(int i=1;i<=m;i++){
    for(int j=m+1;j<=m+n;j++){
        add(i,j,1);
    }
}
for(int i=m+1;i<=m+n;i++){
    int c;cin>>c;
    add(i,T,c);
}

//为什么不dinic()!=0即可?可能只是部分匹配
if(dinic()==sum){
    cout<<1<<endl;
    for(int u=1;u<=m;u++){
        bool flag=1;
   for(int i=head[u];~i;i=edge[i].next){
       if(!edge[i].f&&edge[i].to>=m+1&&edge[i].to<=m+n){
           if(flag)flag=0;
           else cout<<" ";
           cout<<edge[i].to-m;
       }
   }
        cout<<endl;
    }
}
else cout<<0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值