首页 > 代码库 > HDU 3605 Escape(最大流+缩点转换)

HDU 3605 Escape(最大流+缩点转换)

http://acm.hdu.edu.cn/showproblem.php?pid=3605

题目很简单,要求的就是最后能搬到星球上去的人的个数。刚开始看到,知道是最大流,就把人和星球都设为点,能生存就连线,权值为1,最后建立超级源点和超级汇点。求出最大流量即可。先是RE,开大数组后TLE。仔细算了,光光人到星球的便就可达到100w了,超时的概率太大了。后来找了解题报告,知道了缩点这一说,因为星球个数m最大只有10个,所以每个人最多只有1024种情况,把这每一种情况设为点(这里很抽象),将之与符合情况的星球相连。边流量就是这种情况总的人数。最后每个星球以限定居住人数为边流量连超级汇点。建图完成后就可以用最大流求解了。

  1 /*Dinic算法求最大流*/  2 #include<stdio.h>  3 #include<string.h>  4 #define point_MAX 100025  5 #define edge_MAX 10000000  6 #define INF_MAX 999999999  7 #include<iostream>  8 using namespace std;  9 struct EDGE 10 { 11     int to;/*指向的点*/ 12     int next;/*指向的下一条邻边*/ 13     int w;/*权值*/ 14 }edge[edge_MAX]; 15 int len;/*边的数量*/ 16 int point[point_MAX]; 17 int Vertex,Edge; 18 int d[point_MAX]; 19 void init()/*初始化*/ 20 { 21     len=0; 22     memset(point,0,sizeof(point)); 23 } 24 int add_edge(int a,int b,int w)/*添加由a指向b的权值为w的边*/ 25 { 26     len++; 27     edge[len].w=w; 28     edge[len].to=b; 29     edge[len].next=point[a]; 30     point[a]=len; 31     return 0;/*无重边,插入*/ 32 } 33 int bfs(int s) 34 { 35     int q[point_MAX],front=0,rear=1,j,t,i; 36     q[0]=s; 37     memset(d,-1,sizeof(d));/**/ 38     d[s]=0; 39     while(front<rear) 40     { 41        t=q[front++]; 42          for(j=point[t];j;j=edge[j].next) 43          { 44             if(d[edge[j].to]==-1&&edge[j].w>0) 45             { 46              d[edge[j].to]=d[t]+1; 47            q[rear++]=edge[j].to;/*逐层增加*/ 48         } 49          } 50     } 51     if(d[Vertex]>=0) 52        return 1; 53     return 0; 54 } 55 long long min(long long a,long long b) 56 { 57     return a<b?a:b; 58 } 59 long long dinic(int t,long long sum)/*寻找增广路*/ 60 { 61     int i,os,j; 62     long long a; 63     if(t==Vertex)/*如果已经找到汇点,返回sum*/ 64       return sum; 65     os=sum; 66     for(i=point[t];i&&sum;i=edge[i].next) 67     { 68        if(d[edge[i].to]==d[t]+1&&edge[i].w>0)/*可行流,即增广路*/ 69        { 70            a=dinic(edge[i].to,min(sum,edge[i].w)); 71            edge[i].w-=a; 72            for(j=point[edge[i].to];edge[j].to!=t;j=edge[j].next); 73            edge[j].w+=a;/*处理反向边*/ 74            sum-=a; 75        } 76     } 77     return os-sum; 78 } 79 long long DINIC(int s)/*DINIC算法*/ 80 { 81      long long ans=0; 82      while(bfs(s))/*遍历整个图,判断是否已经完成最大流*/ 83        ans+=dinic(s,INF_MAX);/*添加所能增加的流量*/ 84      return ans; 85 } 86  87 int main() 88 { 89     int n,m,x,a[2005]; 90     int s=0,t=1050; 91     while(scanf("%d%d",&n,&m)!=EOF) 92     { 93         init(); 94         memset(a,0,sizeof(a)); 95         for(int i=1;i<=n;i++) 96         { 97             int k=0; 98             for(int j=0;j<m;j++) 99             {100                 scanf("%d",&x);101                 if(x)102                 {103                     k|=1<<j;104                 }105             }106             a[k]++;107         }108 109         for(int i=1;i<=(1<<m);i++)110         {111             if(a[i-1])112             {113                 add_edge(s,i,a[i-1]);114                 add_edge(i,s,0);115                 for(int j=0;j<m;j++)116                     if((i-1)&(1<<j))117                     {118                         add_edge(i,j+10+(1<<m),a[i-1]);119                         add_edge(j+10+(1<<m),i,0);120                     }121             }122         }123         for(int i=0;i<m;i++)124         {125             scanf("%d",&x);126             add_edge(i+(1<<m)+10,t,x);127             add_edge(t,i+(1<<m)+10,0);/*添加反向边*/128 129         }130         Vertex=t;131         int ans=DINIC(0);132         //cout<<ans<<endl;133         if(ans>=n)cout<<"YES"<<endl;134         else cout<<"NO"<<endl;135     }136     return 0;137 }
View Code