【網絡流24題-洛谷-P2756】飛行員配對方案問題(二分圖最大匹配、最大流)

題目背景

第二次世界大戰時期..

題目描述

英國皇家空軍從淪陷國徵募了大量外籍飛行員。由皇家空軍派出的每一架飛機都需要配備在航行技能和語言上能互相配合的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!’。

 

輸入輸出樣例

輸入樣例#1: 複製

5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1

輸出樣例#1: 複製

4
1 7
2 9
3 8
5 10 

解題報告:

這道題的很明顯是要求二分圖的最大匹配,建圖方式就是,建立一個超級源點和一個超級匯點,將源點和所有的外籍飛行員連邊,將英國飛行員和超級匯點連邊,還有就是將給的外籍飛行員和英國飛行員連邊,每條邊的流量都爲1,最後求從超級源點到超級匯點的最大流。最大流的值就是二分圖最大匹配的結果。要輸出哪些邊是匹配的,就遍歷所有的外籍飛行員,然後找邊的終點是英國飛行員並且流量不爲0的點,那麼這條邊就一定被選過了。(從洛谷還看到一個判斷的方法: 判斷邊是否有流量,即判斷反向邊的權值是否不爲0)。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<iostream>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
#define eps 1e-8
using namespace std;
const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
struct Edge {
    int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;//節點總個數,節點編號從 0 ~ N-1
void init(int n) {
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost) {
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s,int t) {
    queue<int>q;
    for(int i = 0; i < N+1; i++) {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost ) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;
    else return true;
}
//返回的是最大流,cost 存的是最小費用
int minCostMaxflow(int s,int t,int &cost) {
    int flow = 0;
    cost = 0;
    while(spfa(s,t)) {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}

int main()
{
    int n,m;
    scanf("%d%d",&m,&n);
    init(n+m+2);
    while(1){
        int a,b;
        scanf("%d%d",&a,&b);
        if(a==-1&&b==-1)break;
        addedge(a,b,1,0);
//		add(b,a,1,0);
    }
    for(int i=1;i<=m;i++)
    {
        addedge(0,i,1,0);
    }
    for(int i=m+1;i<=m+n;i++)
    {
        addedge(i,n+m+1,1,0);
        addedge(n+m+1,i,1,0); 
    }
    int c;
    int ans=minCostMaxflow(0,n+m+1,c);
    if(ans==0){
        puts("No Solution!");
    }
    else
    {
        printf("%d\n",ans);
        for(int i=1;i<=m;i++)
        {
            for(int j=head[i];j!=-1;j=edge[j].next){
                if(edge[j].to!=0&&edge[j].to!=n+m+1&&edge[j].flow>0)
                {
                    printf("%d %d\n",i,edge[j].to);
                    break;
                }
            }
        }
    }
    return 0;
} 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章