poj1087(网络流 + EK)

来源:互联网 发布:足球数据库哪个好 编辑:程序博客网 时间:2024/04/28 15:59

            题目链接:http://poj.org/problem?id=1087

          题意:房间里N个通了电的插座,有M种电器,每种电器只有插在对应类型的插座上才能正常工作。现在有K个转换器,能将一种类型的插座转换成另一种类型的插座。利用这K个转换器和N个通电插座,使尽量多的电器能够正常通电工作(题目要求输出最少能使多少台电器不工作的数目)。

          思路:网络流求最大流(能正常工作的电器数目的最大值)。

          建图过程:在源点与N个通电插座之间建立一条边,权值为该类型插座的数目个数

                             在转换器能转换的两个类型插座之间建立一条边,权值为INF(无限多)

                              在电器与汇点之间建立一条权值为1的边。

           后来发现我建的图和网上通常建的图刚好是一个逆向图,不知道他们是怎么想的,反正我在做这个题的时候是这样想的建图的,不过两种见图的方式最后的结果都是一样的。

            注意:1.每种转换器的数目由无数多个,所以权值应该为INF

                       2.注意在输入电器的时候出现的新型插座和输入转换器时候的新型插座一定要加入到插座类型plug中,我因为一时偷懒,没有处理输入转换器的时候出现的新型插座导致比赛的时候,这个题是没有AC的,忘后来者记住。

代码:

#include<stdio.h>#include<string.h>#define INF 1000000char plug[250][30];char dev[30],nee[30];char adapter[30],plu[30];int cap[505][505],flow[505][505];int dis[505];int que[505],pre[505];  int min(int x,int y)  {      return x<y ? x : y;   }  int EK(int t)  {      int i,temp;        int ans=0;        int head=0,tail=0;        memset(flow,0,sizeof(flow));        while(1)        {            memset(pre,0,sizeof(pre));            memset(dis,0,sizeof(dis));            dis[0]=INF;            que[tail]=0;            tail++;            pre[0]=0;            while(head != tail)            {                 temp=que[head];                 head++;   if(head == 505) head = 0;             for(i=0;i<=t;i++)                 {                     if(!dis[i] && flow[temp][i] < cap[temp][i])                     {                         dis[i] = min(dis[temp],cap[temp][i]-flow[temp][i]);                              pre[i]=temp;                             que[tail]=i;                             tail++;    if(tail == 505) tail = 0;                 }                 }            }            if(dis[t] == 0)                break;            for(i=t;i!=0;i=pre[i])            {                flow[pre[i]][i] += dis[t];                flow[i][pre[i]] -= dis[t];            }            ans += dis[t];        }        return ans;    }  int main(){int i,j;int N,M,K,t;int minnum;memset(cap,0,sizeof(cap));    scanf("%d",&N);for(i=1;i<=N;i++){scanf("%s",plug[i]);if(i==1)cap[0][i] = 1;else{for(j=1;j<i;j++){if(strcmp(plug[i],plug[j]) == 0){cap[0][j] += 1; //一个类型的插座有几个,与源点连边的权值就为几break;}}if(j == i)cap[0][j] = 1;}}scanf("%d",&M);t = 401+M;for(i=401;i<=401+M-1;i++){/*为什么从401开始呢,因为N如果有100种,而M得时候又有100种新型的,到K得时候可能出现200种新型的,所以总共插座类型的话可能有400种,所以电器只能从401开始*/            scanf("%s%s",dev,nee);cap[i][t] = 1;//在电器与汇点之间建立一条权值为1的边for(j=1;j<=N;j++){if(strcmp(nee,plug[j]) == 0){cap[j][i] = 1;break;}}if(j == N+1)//出现新型的插座类型,则加入plug中{N++;strcpy(plug[N],nee);cap[j][i] = 1;}}scanf("%d",&K);while(K--){scanf("%s%s",adapter,plu);            for(i=1;i<=N;i++){if( strcmp(plug[i],plu) == 0 )break;}if(i == N+1)//出现新型的插座类型,则加入plug中{N++;strcpy(plug[N],plu);}for(j=1;j<=N;j++){if( strcmp(plug[j],adapter) == 0 )break;}if(j == N+1)//出现新型的插座类型,则加入plug中{N++;strcpy(plug[N],adapter);}cap[i][j] = INF;//如果plug[j]可以由plug[i]转化而来,则从i连接一条权值为INF的边到j}      minnum = M-EK(t); printf("%d\n",minnum);return 0;}


         

原创粉丝点击