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&∑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 else 129 { 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 3 153 75 15 21 154 75 15 28 155 34 70 5 156 3 157 906 864 217 158 502 517 839 159 996 39 29 160 161 */