作为一个刚入门网络流的弱菜,表示看到这个题,实在建不出图。直接在discuss看的别人怎么建图的。既然看了别人的建图方法,那只能讲讲目前的理解了。
题意: 给定一个 n * m 大小的矩阵, 矩阵中每个格子都有一个不大于 1000 的正整数, 现在要从矩阵中选出若干个格子, 使得得分最大. 得分等于选取的格子里的数字和. 如果有两个格子相邻, 则得分将减去 2 * (x & y), x 和 y 为相邻两个格子内的数字. 此外, 还有一些格子是必选的.
二分图最小点权覆盖的建图方法:
建立超级源点S和超级汇点T,对于二分图X,Y中的点,从S向X连一条容量为X权值的边,从Y向T连一条容量为Y权值的边,其余边容量无穷大。由S->T做一遍最大流。对于该图中任意一个割,那个割所对应的点就是一个可行解,最小权和为割的容量。
在本题中:建立超级源点S和超级汇点T,对行号i+列号j为奇数的点作为二分图X中的点,为偶数的作为二分图中Y中的点,从S向X连边,从Y向T连边,若该节点必选,则容量为+INF,否则,容量为该带你权值,对于从X到Y中的可以相邻的点(就是在原图中有公共边的格子对应的点),容量设置成2*(x&y)。由S->T做一次最大流,最终答案是所有格子中的点的权值之和sum-Maxflow(S->T)。
看完这个建图方法,我瞬间就冒出很多傻逼的问题。。以下是一些自问自答。。答的不一定对。
1.最大流=最小割,这张图中的最小割对应的就是在原矩阵中损失掉的分数。那么在本题中,就是要使分数流失最小。那如果我让总流量小一点,那不是分数损失的更少吗?对于一条边上分数的损失,要么就损失,要么就不损失,损失的时候就一定达到满流。
2.为什么必选点到源汇点的流量必须是+INF?容量无穷,改变不可能成为最小割的边,也以为着这条边一定上的分数不能损失,也就是必选。那么对其他点,容量有限,就有可能出现在最小割中,从而那条边上的值成为被牺牲掉的分数。
3.X,Y之间的点的流量为什么要设置成2*(x&y)?为了限制损失。如果没有满流,就说明损失这条边上的分数不值得。
4.同上,那为什么在二分图最小点权覆盖问题中,X->Y的边上的容量可以设为+INF?同样把流量看成一种损失。容量设置成INF,意味着不用去权衡这条边上的损失,造成损失的根源不在这条边,而是在从源点S到X中的点的那条边上。
这只是现在的理解。。应该有一些不合理的地方。。说不定我以后回过头来看这篇东西,会觉得好搞笑。。
贴代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <vector> 6 #include <algorithm> 7 #define maxn 3000 8 #define maxm 15000 9 #define INF 1<<30 10 using namespace std; 11 12 int n,m,k,S,T,e; 13 int score[55][55]; 14 bool flag[55][55]; 15 int dx[] = {0,-1,0,1}; 16 int dy[] = {1,0,-1,0}; 17 int first[maxn],v[maxm],w[maxm],next[maxm]; 18 int d[maxn],q[maxn],work[maxn]; 19 20 void init(){ 21 memset(first,-1,sizeof(first)); 22 e = 0; 23 } 24 25 void add_edge(int a,int b,int c){ 26 //printf("addedge %d %d %d ",a,b,c); 27 v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++; 28 v[e] = a;next[e] = first[b];w[e] = 0;first[b] = e++; 29 } 30 31 int bfs(){ 32 int i,j,rear = 0; 33 memset(d,-1,sizeof(d)); 34 d[S] = 0;q[rear++] = S; 35 for(int i = 0;i < rear;i++){ 36 for(int j = first[q[i]];j != -1;j = next[j]) 37 if(w[j] && d[v[j]] == -1){ 38 d[v[j]] = d[q[i]] + 1; 39 q[rear++] = v[j]; 40 if(v[j] == T) return 1; 41 } 42 } 43 return 0; 44 } 45 46 int dfs(int cur,int a){ 47 if(cur == T) return a; 48 for(int &i = work[cur];i != -1;i = next[i]) 49 if(w[i] && d[v[i]] == d[cur] + 1) 50 if(int t = dfs(v[i],min(a,w[i]))){ 51 w[i] -= t;w[i^1] += t; 52 return t; 53 } 54 return 0; 55 } 56 57 int dinic(){ 58 int ans = 0,t; 59 while(bfs()){ 60 memcpy(work,first,sizeof(first)); 61 while(t = dfs(S,INF)) ans += t; 62 } 63 return ans; 64 } 65 66 int ID(int row,int column){ 67 if(row < 1 || row > n) return 0; 68 if(column < 1 || column > m) return 0; 69 return (row-1) * m + column; 70 } 71 72 int main() 73 { 74 while(scanf("%d%d%d",&n,&m,&k) == 3){ 75 init(); 76 int sum = 0; 77 S = 0,T = n*m+1; 78 memset(flag,0,sizeof(flag)); 79 for(int i = 1;i <= n;i++){ 80 for(int j = 1;j <= m;j++){ 81 scanf("%d",&score[i][j]); 82 sum += score[i][j]; 83 } 84 } 85 for(int i = 0;i < k;i++){ 86 int a,b; 87 scanf("%d%d",&a,&b); 88 flag[a][b] = true; 89 } 90 for(int i = 1;i <= n;i++){ 91 for(int j = 1;j <= m;j++){ 92 if((i + j) % 2 == 0){ 93 if(flag[i][j]) add_edge(S,ID(i,j),INF); 94 else add_edge(S,ID(i,j),score[i][j]); 95 for(int k = 0;k < 4;k++){ 96 int x = i + dx[k]; 97 int y = j + dy[k]; 98 if(ID(x,y) != 0){ 99 //printf("i = %d,j = %d,k = %d,x = %d,y = %d ",i,j,k,x,y); 100 add_edge(ID(i,j),ID(x,y),2*(score[i][j]&score[x][y])); 101 } 102 } 103 }else{ 104 if(flag[i][j]) add_edge(ID(i,j),T,INF); 105 else add_edge(ID(i,j),T,score[i][j]); 106 } 107 } 108 } 109 int maxflow = dinic(); 110 printf("%d ",sum - maxflow); 111 } 112 return 0; 113 }