1. 程式人生 > >Dining POJ - 3281 (網路流)

Dining POJ - 3281 (網路流)

傳送門

題意:農夫約翰為他的N頭牛準備了F種食物和D種飲料。每頭牛都有各自喜歡的食物和飲料,而每種食物或飲料只能分配給一頭牛。最多能有多少頭牛同時得到自己喜歡的食物和飲料?

題解:如果只是分配食物的話,那麼用二分圖最大匹配就好了,但遇到這種情況需要同時給一頭牛分配所喜歡的食物和飲料的情況,就不能很好的處理了,可以將食物和飲料所對應的兩個匹配通過下面這種方法匹配起來。

圖的頂點在食物對應的匹配中的食物和牛,飲料對應的匹配中的飲料和牛之外,還有一個源點s和一個匯點t。

在兩個匹配相同的牛之間連一條邊,在s和所有食物,t和所有飲料之間連一條邊。

邊的方向為s->食物->牛->牛->飲料->t,容量全都為1。

這個圖中的每一條s-t路徑都對應一個牛的食物和飲料的分配方案。我們把食物所對應的牛和飲料所對應的牛拆成兩個頂點,之間連一條容量為1的邊,就保證了一頭牛不會被分配多組食物和飲料。只要計算該圖中的最大流,問題就解決了。

附上程式碼:


#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>

using namespace std;

const int MAX_N=150;
const int MAX_D=150;
const int MAX_F=150;
const int MAX_V=1000;
const int INF=0x3f3f3f3f;

int N,F,D;
int likeF[MAX_N][MAX_F];
int likeD[MAX_N][MAX_D];

struct edge{
    int to,cap,rev;
    edge(int _to,int _cap,int _rev):to(_to),cap(_cap),rev(_rev){}
};

vector<edge>G[MAX_V];
int level[MAX_V];
int iter[MAX_V];


void add_edge(int from,int to,int cap)
{
    G[from].push_back(edge(to,cap,G[to].size()));
    G[to].push_back(edge(from,0,G[from].size()-1));
}

void bfs(int s)
{
    memset(level,-1,sizeof(level));
    queue<int>que;
    level[s]=0;
    que.push(s);
    while(!que.empty()){
        int v=que.front();que.pop();
        for(int i=0;i<G[v].size();i++){
            edge &e=G[v][i];
            if(e.cap>0&&level[e.to]<0){
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int v,int t,int f)
{
    if(v==t){
        return f;
    }
    for(int &i=iter[v];i<G[v].size();i++){
        edge &e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s,int t)
{
    int flow=0;
    for(;;){
        bfs(s);
        if(level[t]<0){
            return flow;
        }
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,INF))>0){
            flow+=f;
        }
    }
}

void solve()
{
    int s=N*2+F+D,t=s+1;
    for(int i=0;i<F;i++){
        add_edge(s,N*2+i,1);
    }
    for(int i=0;i<D;i++){
        add_edge(N*2+F+i,t,1);
    }
    for(int i=0;i<N;i++){
        add_edge(i,N+i,1);
        for(int j=0;j<F;j++){
            if(likeF[i][j]){
                add_edge(N*2+j,i,1);
            }
        }
        for(int j=0;j<D;j++){
            if(likeD[i][j]){
                add_edge(N+i,N*2+F+j,1);
            }
        }
    }
    printf("%d\n",max_flow(s,t));
}

int main()
{
    scanf("%d%d%d",&N,&F,&D);
    int cntf,cntd;
    for(int i=0;i<N;i++){
        scanf("%d%d",&cntf,&cntd);
        int temp;
        for(int j=0;j<cntf;j++){
            scanf("%d",&temp);
            temp--;
            likeF[i][temp]=1;
        }
        for(int j=0;j<cntd;j++){
            scanf("%d",&temp);
            temp--;
            likeD[i][temp]=1;
        }
    }
    solve();
    return 0;
}