1. 程式人生 > >hihocoder1393 網路流之二分圖多重匹配

hihocoder1393 網路流之二分圖多重匹配

題目連結:http://hihocoder.com/problemset/problem/1393#


題目大意:

學校的秋季運動會即將開始,為了決定參賽人員,各個班又開始忙碌起來。

小Hi和小Ho作為班上的班幹部,統計分配比賽選手的重任也自然交到了他們手上。

已知小Hi和小Ho所在的班級一共有N名學生(包含小Hi和小Ho),編號依次為1..N。

運動會一共有M項不同的比賽,編號為1..M。第i項比賽每個班需要派出m[i]名選手參加。

根據小Hi和小Ho的統計,編號為i的學生表示最多同時參加a[i]項比賽,並且給出他所擅長的b[i]項比賽的編號。

小Hi和小Ho希望將每個學生都安排到他所擅長的比賽專案,以增加奪冠的可能性。同時又要考慮滿足每項比賽對人數的要求,當然給一個學生安排的比賽專案也不能超過他願意參加的比賽專案數量。

根據統計的結果,小Hi和小Ho想知道能否有一個合適的安排,同時滿足這些條件。

提示:二分圖多重匹配

輸入

第1行:1個整數T,表示一共有T(2≤T≤5)組資料,每組資料按如下格式給出:

第1行:2個正整數N,M。1≤N≤100,1≤M≤100。

第2行:M個整數,第i個數表示第i個專案需要的選手數量m[i]。1≤m[i]≤N。

第3..N+2行:若干整數,第i+2行表示編號為i的學生的資訊。先是a[i],b[i],接下來b[i]個整數,表示其所擅長的b[i]個專案。1≤a[i]≤M

輸出

第1..T行:第i行表示第i組資料能否滿足要求,若能夠輸出"Yes",否則輸出"No"。



思路:

小Ho:真是麻煩啊,要不讓他們自己報名專案怎麼樣?

小Hi:那樣可能滿足不了人數的要求啊,還是根據他們的意願來進行分配吧。

小Ho:要是每個專案都只要派一個人參加就好了。這樣只需要一個簡單的二分圖匹配就可以決定了!

小Hi:但是現在的情況是一個人可以參加多個專案,一個專案也需要多個人參加才行。

小Ho:對啊,所以這就問題所在啊。小Hi你有什麼解決的辦法麼?

小Hi:嗯......有了!

小Ho:什麼辦法?

小Hi:還是從你剛剛說的二分圖匹配入手,我們仍然將學生看作A部,比賽專案看作B部:

接下來,根據每個學生的意願我們將對應的A[i]和B[j]連起來。比如編號為i的學生擅長編號為j的專案,那麼就連線A[i]-B[j]:

小Ho:然後呢?

小Hi:這次的問題和二分匹配不同的地方在於:匹配結果中,A[i]可能和多個B[j]相連,如果有一個可以來記錄A[i]連線數量的量呢?

小Ho:記錄A[i]的連線數量麼?

小Hi:對!因為A[i]最多隻能和a[i]的點相連。所以我們需要一個方法來限制這個量在0~a[i]之間變動。

小Ho:這聽上去有點像網路流裡面的流量,只能在0和容量之間變動。

小Hi:沒錯!這裡我們要用的就是網路流。

小Ho:那具體怎麼做呢?

小Hi:我們需要對之前的二分圖進行進一步擴充套件。首先虛擬一個源點s和匯點t:

接下來我們根據a[i],在s和A[i]之間連線一條容量為a[i]的邊。

同時將原來A[i]-B[j]直接的邊都改造為從A[i]到B[j]的容量為1的邊。

最後我們還要連線所有的B[j]-t。由於比賽專案B[j]最多隻需要m[j]名選手參加,所以我們不妨限制B[j]-t的容量為m[j]。

這就是我們最後的網路流圖。那麼小Ho,你想想在這個圖上面做的最大流有什麼含義?

小Ho:我想想啊......我知道了!

在完成最大流後,這個網路流圖的流量情況直接對應了一個分配方案:

s-A[i]:這一類邊的流量表示了A[i]參加的專案數量。

A[i]-B[j]:這一類邊的流量表示了A[i]是否參加專案B[j],若參加則流量為1,否則流量為0。

B[j]-t:這一類邊的流量表示了參加比賽專案B[j]的選手數量。

由於流網路會自動調整去滿足最大流量,所以它會自動調整每個A[i]-B[j]的流量來使得B[j]-t儘可能大。

若每個專案都能夠滿足人數的話,網路流會自己調整為所有B[j]-t都滿流的情況。

小Hi:就是這樣,我們只需要最後判斷一下每一條B[j]-t的邊是否滿流,就可以知道能否滿足需求。同時還可以根據A[i]-B[j]的情況,計算出每個選手所參加的比賽專案。

小Ho:這個方法好厲害,都不需要自己手動去進行調整了。

小Hi:沒錯,這也就是網路流的神奇之處。對於這類允許多條邊的匹配問題,也被稱為二分圖多重匹配問題。而網路流正是它的一個簡單解決方法。

小Ho:我明白了!我這就去實現它,然後趕緊把報名表交上去!



#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <stdlib.h>
#include <iomanip>
#include <fstream>

using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 105
#define MOD 1000000007
#define mod 2147493647
#define mem(a , b) memset(a , b , sizeof(a))
#define LL long long
#define ULL unsigned long long
#define FOR(i , n) for(int i = 1 ;  i<= n ; i ++)
typedef pair<int , int> pii;
int n , m , t;
int a[maxn] , b[maxn] , M[maxn] , path[2*maxn];
int e[2*maxn][2*maxn] , vis[2*maxn];
int flow[2*maxn];
bool bfs(int ed)
{
    mem(path , -1);
    mem(vis , 0);
    mem(flow , 0);
    flow[0] = MOD;
    queue<int>q;
    while(!q.empty()) q.pop();
    q.push(0) ;
    vis[0] = 1;
    while(!q.empty())
    {
        int cur = q.front();
        q.pop();
        if(cur == ed) return 1;
        for(int i = 0 ; i <= ed; i ++)
        {
            if(!vis[i] && e[cur][i] > 0)
            {
                path[i] = cur;
                vis[i] = 1;
                flow[i] = min(flow[cur] , e[cur][i]);
                q.push(i);
            }
        }
    }
    return 0;
}

int main()
{
    scanf("%d" , &t);
    while(t--)
    {
        mem(e , 0);
        scanf("%d %d" , &n , &m);
        int st = 0 , ed = n+m+1;
        for(int i = 1 ; i <= m ; i ++)
        {
            scanf("%d" , &M[i]);
            e[n+i][ed] += M[i];
        }
        for(int i = 1 ; i <= n ; i ++)
        {
            scanf("%d %d" , &a[i] , &b[i]);
            e[0][i] += a[i];
            int v;
            for(int j = 0 ; j < b[i] ; j ++)
            {
                scanf("%d" , &v);
                e[i][n+v] += 1;
            }
        }
        while(bfs(ed))
        {
            int f = flow[ed];
            int u  = ed , v = path[u];
            while(v != -1)
            {
                e[u][v] += f;
                e[v][u] -= f;
                u = v;
                v = path[u];
            }
        }
        int flag = 0;
        for(int i = n + 1 ; i <= n + m ; i ++)
        {
            if(e[i][ed] != 0)
            {
                flag = 1;
                break;
            }
        }
        if(!flag) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}