首页 > 代码库 > HDU 1565 (最大流+黑白染色化二分图求最小割)

HDU 1565 (最大流+黑白染色化二分图求最小割)

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

思路:将横纵坐标和为偶尔染白色,其他染黑色,黑点连接源点,流量为该点的值,白点连接汇点,流量为该点的值,黑白点有相邻的就连边,值为无穷大。最后求最大流,即该图的最小割。

PS:刚开始不明白为为什么最大流会等于最小割,为什么所有的点之和减去最小割就会等于答案。

我的理解是:整张图其实就跟连接管道一样,连接了黑点表示取了黑点那个值的流量,白点也是,而连接了相邻的黑白点求出的最大流就会是流量较小的那个的值。好比三根管道连接,流量分别是white,black,inf_max,这样求出来的肯定是min(white,black,inf_max),所以只要去掉这些割边所对应的值的和,剩下的就是答案的最大值了。

  1 /*Dinic算法求最大流*/  2 #include<stdio.h>  3 #include<string.h>  4 #include<iostream>  5 #define point_MAX 10000  6 #define edge_MAX 100000  7 #define INF_MAX 999999999  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 int abs(int a) 87 { 88     return a>0?a:(-a); 89 } 90 int main() 91 { 92      int i,j,x,y,w,S,D; 93      int cost[25][25],num[25][25],col[25][25]; 94      while(scanf("%d",&Vertex)!=EOF) 95      { 96         init(); 97         int NUM=1; 98         long long sum=0; 99         for(i=1;i<=Vertex;i++)100             for(j=1;j<=Vertex;j++)101             {102                 scanf("%d",&cost[i][j]);103                 sum+=cost[i][j];104                 num[i][j]=NUM++;105                 if((i+j)&1)col[i][j]=1;//黑白染色,即化为二分图106                 else col[i][j]=0;107             }108         for(i=1;i<=Vertex;i++)109         {110             for(j=1;j<=Vertex;j++)111             {112                 int s=num[i][j];113                 if(col[i][j]&1)//奇数点/黑点连在源点上114                 {115                     add_edge(0,s,cost[i][j]);116                     add_edge(s,0,0);//反向边117                     for(int k=1;k<=Vertex;k++)118                         for(int l=1;l<=Vertex;l++)119                         {120                             if(abs(i-k)+abs(j-l)==1&&!(col[k][l]&1))121                             {122                                 //printf("%d->%d\n",s,num[k][l]);123                                 add_edge(s,num[k][l],INF_MAX);124                                 add_edge(num[k][l],s,0);125                             }126                         }127                 }128                 else129                 {130                     add_edge(s,NUM,cost[i][j]);131                     add_edge(NUM,s,0);//反向边132                     /*for(int k=1;k<=Vertex;k++)133                         for(int l=1;l<=Vertex;l++)134                         {135                             if(abs(i-k)+abs(j-l)==1&&(num[k][l]&1))136                             {137                                 //printf("%d->%d\n",s,num[k][l]);138                                 add_edge(num[k][l],s,INF_MAX);139                                 add_edge(s,num[k][l],0);140                             }141                         }*/142                 }143             }144         }145         Vertex=NUM;146         //cout<<"=="<<endl;147         cout<<sum-DINIC(0)<<endl;/*以S为源点,Vertex为汇点*/148      }149      return 0;150 }151 /*152 3153 75 15 21154 75 15 28155 34 70 5156 3157 906 864 217158 502 517 839159 996 39 29160 161 */
View Code