POJ 3281 Dining

这个题写了一天,现在才过......

一开始想到了二分图的最大匹配,每种组合连边,算出最多的。结果仔细一想是错的,因为没有考虑到一个牛只能选一组。

然后就想到开始建图跑最大流了,一开始采用了 源点-->牛-->食物in-->食物out-->饮料-->汇点  这样的模式建图。结果一直WA,看了Discuss,发现是错的。

拆食物错的原因在于:
如果拆食物,按照牛->食物-in->食物-out->饮料 这样建图的话,虽然保证了流量的限制,每头牛也只能选一种食物和饮料。
但是食物-out->饮料之间的边就失去的牛的编号性,就成了没有针对哪头牛的组合,不知道某些边是哪些牛的喜好。
这样就会导致某头牛明明不喜欢某种搭配,但是因为有其他牛喜欢这种搭配,使得这头牛“强行”被选择了这种搭配。自然就WA了。

然后就只能用  源点-->食物-->牛in-->牛out-->饮料--汇点 这样的模式建图。

恶心的是,刘汝佳白书的Dinic居然TLE了... ...然后找了个非递归的Dinic AC了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1000;
const int inf=1<<20;
//******************************************//
struct Node
{
    int v,next;
    int val;
}edge[maxn*maxn*2];

int level[maxn];//顶点的层次
int head[maxn];
int que[maxn*10];//BFS中用于遍历的顶点,DFS求增广中记录边
int out[10*maxn];//DFS用于几乎定点的分支
int ind;

void init()
{
    ind=0;
    memset(head,-1,sizeof(head));
}
void addedge(int x,int y,int z)
{
    edge[ind].v=y;
    edge[ind].val=z;
    edge[ind].next=head[x];
    head[x]=ind++;
    edge[ind].v=x;
    edge[ind].val=0;
    edge[ind].next=head[y];
    head[y]=ind++;
}
int dinic(int n,int source,int sink)
{
    int ret=0;
    int h=0,r=0;
    while(1)//DFS
    {
        int i;
        for(i=0;i<=n;++i)
            level[i]=0;
        h=0,r=0;
        level[source]=1;
        que[0]=source;
        while(h<=r)//BFS
        {
            int  t=que[h++];
            for(i=head[t];i!=-1;i=edge[i].next)
                {
                    if(edge[i].val&&level[edge[i].v]==0)
                        {
                          level[edge[i].v]=level[t]+1;
                          que[++r]=edge[i].v;
                        }
                }
        }

        if(level[sink]==0)break;//找不到汇点
        for(i=0;i<=n;++i)
          out[i]=head[i];

        int  q=-1;
        while(1)
        {
            if(q<0)
            {
                int cur=out[source];
                for(;cur!=-1;cur=edge[cur].next)
                {
                    if(edge[cur].val&&out[edge[cur].v]!=-1&&level[edge[cur].v]==2)
                    {
                        break;
                    }
                }
                if(cur>=0)
                {
                    que[++q]=cur;
                    out[source]=edge[cur].next;
                }
                else
                    break;
            }
            int  u=edge[que[q]].v;
            if(u==sink)//一条增广路
            {
                int  dd=inf;
                int  index=-1;
                for(i=0;i<=q;i++)
                {
                    if(dd>edge[que[i]].val)
                    {
                        dd=edge[que[i]].val;
                        index=i;
                    }
                }
                ret+=dd;
                for(i=0;i<=q;i++)
                {
                    edge[que[i]].val-=dd;
                    edge[que[i]^1].val+=dd;
                }
                for(i=0;i<=q;i++)
                {
                    if(edge[que[i]].val==0)
                    {
                        q=index-1;
                        break;
                    }
                }
            }
            else
            {
                int cur=out[u];
                for(;cur!=-1;cur=edge[cur].next)
                {
                    if(edge[cur].val&&out[edge[cur].v]!=-1&&level[u]+1==level[edge[cur].v])
                    {
                        break;
                    }
                }
                if(cur!=-1)
                {
                    que[++q]=cur;
                    out[u]=edge[cur].next;
                }
                else
                {
                    out[u]=-1;
                    q--;
                }
            }
        }
    }
    return ret;
}
int N,F,D,ff,dd;
int FF[maxn],DD[maxn];

//输入输出
int main()
{
    while(~scanf("%d%d%d", &N, &F, &D))
    {
        init();

      //  s=0;
     //   t=2*N+F+D+1;
        for(int i=2*N+1;i<=2*N+F;i++)
            addedge(0,i,1);
        for(int i=2*N+1+F;i<=2*N+F+D;i++)
            addedge(i,2*N+F+D+1,1);
        for(int i=1;i<=N;i++)
            addedge(i,i+N,1);
        for(int i=1;i<=N;i++)
        {
            scanf("%d%d",&ff,&dd);
            for(int j=1;j<=ff;j++) scanf("%d",&FF[j]);
            for(int j=1;j<=dd;j++) scanf("%d",&DD[j]);
            for(int j=1;j<=ff;j++)
                for(int k=1;k<=dd;k++)
                {
                    addedge(2*N+FF[j],i,1);
                    addedge(i+N,2*N+F+DD[k],1);
                }
        }
        printf("%d
",dinic(2*N+F+D+10,0,2*N+F+D+1));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zufezzt/p/4734557.html