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;
}

代码分析:我是参考了别人的思路写出来的,个人觉得很精妙,牛化为两个子集,保证了只有一次选择的机会.很巧的.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章