1. 程式人生 > >【網路流24題之一】飛行員配對問題+求方案(網路流dinic演算法求二分圖最大匹配)

【網路流24題之一】飛行員配對問題+求方案(網路流dinic演算法求二分圖最大匹配)

題面

題目背景
第二次世界大戰時期..
題目描述
英國皇家空軍從淪陷國徵募了大量外籍飛行員。由皇家空軍派出的每一架飛機都需要配備在航行技能和語言上能互相配合的2 名飛行員,其中1 名是英國飛行員,另1名是外籍飛行員。在眾多的飛行員中,每一名外籍飛行員都可以與其他若干名英國飛行員很好地配合。如何選擇配對飛行的飛行員才能使一次派出最多的飛機。對於給定的外籍飛行員與英國飛行員的配合情況,試設計一個演算法找出最佳飛行員配對方案,使皇家空軍一次能派出最多的飛機。
對於給定的外籍飛行員與英國飛行員的配合情況,程式設計找出一個最佳飛行員配對方案,使皇家空軍一次能派出最多的飛機。
輸入輸出格式
輸入格式:
第 1 行有 2 個正整數 m 和 n。n 是皇家空軍的飛行員總數(n<100);m 是外籍飛行員數(m<=n)。外籍飛行員編號為 1~m;英國飛行員編號為 m+1~n。
接下來每行有 2 個正整數 i 和 j,表示外籍飛行員 i 可以和英國飛行員 j 配合。最後以 2個-1 結束。
輸出格式:
第 1 行是最佳飛行員配對方案一次能派出的最多的飛機數 M。接下來 M 行是最佳飛行員配對方案。每行有 2個正整數 i 和 j,表示在最佳飛行員配對方案中,飛行員 i 和飛行員 j 配對。如果所求的最佳飛行員配對方案不存在,則輸出‘No Solution!’。

題解

二分圖求最大匹配的裸題
我們這裡採用轉化為最大流,用當前弧優化的dinic演算法求解。
具體建模:建立一個超級源點S,建立一個超級匯點T,從S到所有的外籍飛行員連一條正向容量為1的邊,所有的英國飛行員連一條正向容量為1的邊,對於題中給出的關係,對於每一對關係,從外籍飛行員連一條到英國飛行員正向容量為1的邊。

code

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int num=0;char c=' ';bool flag=true;
    for(;c>'9'||c<'0'
;c=getchar()) if(c=='-') flag=false; for(;c>='0'&&c<='9';num=(num<<3)+(num<<1)+c-48,c=getchar()); return flag ? num : -num; } namespace graph{ const int maxn=120; struct node{ int y,next,val; }a[maxn*maxn]; int head[maxn],top=0
; void insert(int x,int y,int v){ a[top].y=y; a[top].next=head[x]; a[top].val=v; head[x]=top++; }//插入邊 int n,m; void init(){ m=read(); n=read(); int x,y; memset(head,-1,sizeof head); for(x=read(),y=read();x!=-1&&y!=-1;x=read(),y=read()){ insert(x,y,1); insert(y,x,0); } for(int i=1;i<=m;i++){ insert(0,i,1); insert(i,0,0); }//0是超級源點 for(int i=m+1;i<=n;i++){ insert(i,n+1,1); insert(n+1,i,0); }//n+1是超級匯點 } }using namespace graph; namespace Flow{ int vis[maxn],cur[maxn];//cur陣列:當前弧優化 bool bfs(){ memset(vis,0,sizeof vis); queue<int>q; q.push(0); vis[0]=1; while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i+1;i=a[i].next) if(!vis[a[i].y] && a[i].val!=0){ int v=a[i].y; vis[v]=vis[u]+1; q.push(v); if(v==n+1)return true; } } return false; } int dfs(int x,int flow){ if(x==n+1)return flow; for(int &i=cur[x];i+1;i=a[i].next) //用當前弧優化這裡加地址引用符號& if(a[i].val!=0 && vis[a[i].y]==vis[x]+1){ int y=a[i].y; int k=dfs(y,min(flow,a[i].val)); if(k>0){ a[i].val-=k; a[i^1].val+=k; return k; } } return 0; } int ans=0; void dinic(){ int flow=0; while(bfs()){ for(int i=0;i<=n+1;i++) cur[i]=head[i]; while(flow=(dfs(0,0x3F3F3F3F))) ans+=flow; } printf("%d\n",ans); } }using namespace Flow; void print(){ bool vis[maxn]; memset(vis,0,sizeof vis); for(int i=1;i<top;i+=2) if(a[i].y!=0&&a[i^1].y!=0&&a[i].y!=n+1&&a[i^1].y!=n+1&&a[i].val!=0) printf("%d %d\n",a[i].y,a[i^1].y); //輸出方案,列舉所有的反向邊,如果不是0, //那麼就說明有流量,有流量就說明利用了該對匹配,需要輸出 } int main(){ init();dinic(); if(ans==0)printf("No Solution!\n"); else print(); return 0; }