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

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/83064443