Dining-最大流/Dinic

Dining
Source:POJ-3281

Description
Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others.Farmer John has cooked fabulous meals for his cows, but he forgot to check his menu against their preferences. Although he might not be able to stuff everybody, he wants to give a complete meal of both food and drink to as many cows as possible.Farmer John has cooked F (1 ≤ F ≤ 100) types of foods and prepared D (1 ≤ D ≤ 100) types of drinks. Each of his N (1 ≤ N ≤ 100) cows has decided whether she is willing to eat a particular food or drink a particular drink. Farmer John must assign a food type and a drink type to each cow to maximize the number of cows who get both.Each dish or drink can only be consumed by one cow (i.e., once food type 2 is assigned to a cow, no other cow can be assigned food type 2).

Input
Line 1: Three space-separated integers: N, F, and D
Lines 2..N+1: Each line i starts with a two integers Fi and Di, the number of dishes that cow i likes and the number of drinks that cow i likes. The next Fi integers denote the dishes that cow i will eat, and the Di integers following that denote the drinks that cow i will drink.

Output
Line 1: A single integer that is the maximum number of cows that can be fed both food and drink that conform to their wishes

Sample Input
4 3 3
2 2 1 2 3 1
2 2 2 3 1 2
2 2 1 3 1 2
2 1 1 3 3

Sample Output
3

Hint
One way to satisfy three cows is:
Cow 1: no meal
Cow 2: Food #2, Drink #2
Cow 3: Food #1, Drink #1
Cow 4: Food #3, Drink #3

源代碼:
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

queue <int> Q;
const int MAX = 600;
const int INF = 1<<30;
int  C , F , D , Fi , Di , S , T;
int  mark[MAX] , level[MAX];
int  map[MAX][MAX];
//C是牛的數目,F是食物的數目,D是飲料的數目,S是源點,T是匯點
//圖爲源點->食物->左牛->右牛->飲料->匯點
//兩個牛集合保證了每頭牛隻能選擇一批(一個食物和一瓶飲料)物品

int  Dinic( void );
int  bfs( void );
int  dfs( int u , int flow );
int  min( int a , int b ){ return a <= b ? a : b; }

int main( ){
    int i , j , k , ff , dd;

    while( cin>>C>>F>>D ){
        S = 0 ;        //源點爲0
        T = F + C + C + D + 1;    //匯點爲總點數+1
        memset( map , 0 , sizeof( map ) );    //地圖初始化

        for( i=1 ; i<=F ; i++ )
            map[S][i] = 1;    //源點->食物

        for( k=1 ; k<=C ; k++ ){
            cin>>Fi>>Di;

            for( j=1 ; j<=Fi ; j++ ){    //食物->左牛集合
                cin>>ff;
                map[ff][k+F] = 1;
            }

            map[k+F][k+F+C] = 1;    //左牛集合->右牛集合

            for( j=1 ; j<=Di ; j++ ){
                cin>>dd;
                map[k+F+C][dd+F+C+C] = 1;    //右牛集合->飲料
            }
        }

        for( i=1 ; i<=D ; i++ )
            map[i+F+2*C][T] = 1;    //飲料->匯點

        cout<<Dinic( );
    }

    return 0;
}

int  Dinic( void ){
    int sum=0;

    while( bfs( ) ){
        sum += dfs( S , INF );
    }

    return sum;
}

int  bfs( void ){
    int u , v;

    memset( mark , 0 , sizeof( mark ) );
    memset( level , 0 , sizeof( level ) );

    while( !Q.empty() )
        Q.pop( );

    Q.push( S );
    mark[S] = 1;

    while( !Q.empty() ){
        u = Q.front( );
        Q.pop( );

        if( u==T )
            return 1;

        for( v=S ; v<=T ; v++ ){
            if( !mark[v] && map[u][v] ){
                Q.push( v );
                mark[v] = 1;
                level[v] = level[u] + 1;    //層數加一
            }
        }
    }

    return 0;
}

int  dfs( int u , int flow ){
    int v , flowSum , sum=0;

    if( u==T )
        return flow;

    for( v=S ; v<=T ; v++ ){
        if( level[v]==level[u]+1 && map[u][v] ){
            flowSum = dfs( v , min( flow , map[u][v] ) );
            map[u][v] -= flowSum;
            map[v][u] += flowSum;
            flow -= flowSum;        //流量值減少
            sum += flowSum;
        }
    }

    return sum;
}

代碼分析:我是參考了別人的思路寫出來的,個人覺得很精妙,牛化爲兩個子集,保證了只有一次選擇的機會.很巧的.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章