poj 2289 网络流 and 二分查找

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 2000
#define M 1000010
#define inf 1<<30
using namespace std;
struct Edge{
    int to,val,next;
}edge[M];
int index[N],d[N],gap[N],e,list[N][510];
void addedge(int from,int to,int val)
{
    edge[e].to=to;
    edge[e].val=val;
    edge[e].next=index[from];
    index[from]=e++;
    edge[e].to=from;
    edge[e].val=0;
    edge[e].next=index[to];
    index[to]=e++;
}
int source,des,n,m;
int dfs(int pos,int flow)
{
    if(pos==des)
        return flow;
    int i,j,v,val,lv,mind,c;
    mind=n-1;//初始最小标号为n-1
    lv=flow;
    for(i=index[pos];i!=-1;i=edge[i].next)
    {
        v=edge[i].to;
        val=edge[i].val;
        if(val)
        {
            if(d[v]+1==d[pos])
            {
                c=min(lv,val);//对于该点的最小可行流
                c=dfs(v,c);
                edge[i].val-=c;//更新剩余图
                edge[i^1].val+=c;
                lv-=c;
                if(d[source]>=n)return flow-lv;
                if(lv==0) break;
            }
            if(d[v]<mind)mind=d[v];//找出与pos相连的点的最小标号
        }
    }
    if(lv==flow)//没有找到增广路劲,进行标号更新
    {
        --gap[d[pos]];
        if(!gap[d[pos]])
            d[source]=n;
        d[pos]=mind+1;
        ++gap[d[pos]];
    }
    return flow-lv;
}
int sap(int st,int de)
{
    source=st;
    des=de;
    memset(d,0,sizeof(d));
    memset(gap,0,sizeof(gap));
    gap[0]=n;//初始标号为0的有n个.
    int ans=0;
    while(d[st]<n)
    {
        ans+=dfs(st,inf);
        //cout<<d[st]<<endl;
    }
    return ans;
}
void init()
{
    e=0;
    memset(index,-1,sizeof(index));
    memset(list,0,sizeof(list));
}
// n is the number of point
int main()
{
    int i,j,k,num,pos;
    char str[20],c;
    while(scanf("%d%d",&n,&m)!=EOF,n||m)
    {
        init();
        for(i=1;i<=n;i++)
        {
            pos=0;
            scanf("%s",&str);
            while(scanf("%c",&c),c!='\n')
            {
                scanf("%d",&k);
                list[i][pos++]=k;
            }
            list[i][pos]=-1;
        }
        num=n;
        n=n+m+2;//n是点的数目,一共n+m+2个点。包括源点和汇点
        int l=1,r=1000,mid;
        while(l<r)
        {
            mid=(l+r)>>1;
            memset(index,-1,sizeof(index));
            for(i=1;i<=num;i++)
            {
                addedge(0,i,1);
                pos=0;
                while(list[i][pos]!=-1)
                {
                    addedge(i,num+list[i][pos++]+1,1);
                }
            }
            for(i=1;i<=m;i++)
                addedge(num+i,num+m+1,mid);
            int ans=sap(0,num+m+1);
            //cout<<ans<<endl;
            if(ans<num)
                l=mid+1,e=0;
            else
                r=mid,e=0;
        }
        printf("%d\n",l);
    }
    return 0;
}

 

建图方式:

建立一个超级源点和一个超级汇点,由源点向每个人(编号从1->n)建立一个流量为1的边,从每个人向每个他从属的组织建立一条流量为1的边,在由每个组织建立一条流向汇点的边,我们可以二分枚举组织向汇点边的流量。

 

posted @ 2013-07-16 16:23  fangguo  阅读(181)  评论(0编辑  收藏  举报