[网络流24题] 方格取数问题
题解:
我们观察到题目要求相邻的格子只能选一个,那么我们能想到什么呢?
最大点权独立集!
但是怎么建图?
我们首先对格子进行黑白染色,这样就构成了一个二分图,染色后连边。
1,s ---> 白色, w = 权值
2,黑色 ---> t , w = 权值
3,白色 ---> 黑色 , w = inf
连完之后跑网络流即可。
ans = 权值和 - 最大流
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define AC 10000+200 4 #define ACway 60000+600 //~~~~~~此处为了方便调试已经缩小一百倍,记得修改 5 int Head[AC],good[AC],have[AC],q[AC],head,tail,tot,s,t,m,M[110][110],n,x,ans,addflow,c[AC],last[AC],key; 6 int date[ACway],Next[ACway],haveflow[ACway]; 7 bool co[110][110]; 8 int Min(int a,int b) 9 { 10 int c=(b-a)>>31; 11 return (a&~c)|(b&c); 12 } 13 void add(int f,int w,int S) 14 { 15 //if(f==t&&w==s)printf("``%d\n",tot); 16 // printf("@@%d %d %d\n",f,w,s); 17 date[++tot]=w; 18 haveflow[tot]=S; 19 Next[tot]=Head[f]; 20 Head[f]=tot; 21 } 22 int read() 23 { 24 int x=0;char c=getchar(); 25 while(c>'9'||c<'0')c=getchar(); 26 while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); 27 return x; 28 } 29 void pre() 30 { 31 int now; 32 tot=1; 33 n=read(),m=read(); 34 if(!(m%2))key=m+1; 35 else key=m;//强行扩充为奇数列 36 //要把整个图分成2个部分,一个放在前驱集合,一个放在后继集合,那么一共n*m+2个点 37 s=n*key+1,t=n*key+2; 38 for(int i=1;i<=n;i++)//读入并初始化ans 39 for(int j=1;j<=m;j++) 40 { 41 M[i][j]=read(); 42 ans+=M[i][j]; 43 } 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=m;j++) 46 { 47 now=(i-1)*key+j;//(i-1)代表上面有多少行,所以应该要乘列数,然后加上这行的 48 //printf("%d %d\n",i,j); 49 if(now%2)//因为是把整个图给分开了,所以点也是分成两部分, 50 { 51 //由于将列数强行扩充到奇数,所以相邻的点奇偶性会不同 52 //又因为只有相邻才冲突,所以只有奇偶不同才连边, 53 //这里把奇数放在前驱集合,也就是说只有奇数会向外连边(防止重复) 54 if(i!=n)//如果不是最后一行就向下连边 55 { 56 add(now,now+key,INT_MAX); 57 add(now+key,now,0); 58 } 59 if(i!=1) 60 { 61 add(now,now-key,INT_MAX); 62 add(now-key,now,0); 63 } 64 if(j!=m)//如果不是最后一列就向右连边 65 { 66 add(now,now+1,INT_MAX);//仅仅起到连接作用,控制边权依靠s和t 67 add(now+1,now,0); 68 } 69 if(j!=1)//为防止漏边,是最后一列就向左连边,上同 70 { 71 add(now,now-1,INT_MAX); 72 add(now-1,now,0); 73 } 74 add(s,now,M[i][j]);//添加每个点和源点的连边 75 add(now,s,0); 76 } 77 else 78 { 79 add(now,t,M[i][j]);//添加每个点和后继集合的连边 80 add(t,now,0); 81 } 82 } 83 } 84 void bfs() 85 { 86 int x=t,now; 87 c[t]=1,q[++tail]=t,have[1]=1; 88 memcpy(good,Head,sizeof(Head)); 89 while(head<tail) 90 { 91 x=q[++head]; 92 for(int i=Head[x]; i ;i=Next[i]) 93 { 94 now=date[i]; 95 if(!c[now] && haveflow[i^1]) 96 { 97 c[now]=c[x]+1; 98 q[++tail]=now; 99 have[c[now]]++; 100 } 101 } 102 } 103 } 104 void aru() 105 { 106 // printf("%d <--- ",t); 107 while(x!=s) 108 { 109 haveflow[last[x]]-=addflow; 110 haveflow[last[x]^1]+=addflow; 111 x=date[last[x]^1]; 112 // printf("%d <--- ",x); 113 } 114 // printf("the addflow is %d ",addflow); 115 // printf("\n"); 116 ans-=addflow; 117 118 } 119 void isap() 120 { 121 bool done;int now; 122 x=s,addflow=INT_MAX; 123 while(c[s]!=n*key+3)//~~~~~~~~记得修改 124 { 125 if(x==t)aru(),addflow=INT_MAX; 126 done=false; 127 for(int i=good[x]; i ;i=Next[i]) 128 { 129 now=date[i]; 130 if(haveflow[i] && c[now]==c[x]-1) 131 { 132 good[x]=i; 133 done=true; 134 addflow=Min(addflow,haveflow[i]); 135 last[now]=i; 136 x=now; 137 break; 138 } 139 } 140 if(!done) 141 { 142 int go=n*key+2;//记得修改 143 for(int i=Head[x]; i ;i=Next[i])//Head[x]!!!!!!!!!!!!!!!!!!! 144 { 145 if(haveflow[i] && c[date[i]])go=Min(go,c[date[i]]); 146 } 147 good[x]=Head[x]; 148 if(!(--have[c[x]]))break; 149 have[c[x]=go+1]++; 150 if(x!=s)x=date[last[x]^1]; 151 } 152 } 153 printf("%d\n",ans); 154 } 155 int main() 156 { 157 pre(); 158 bfs(); 159 isap(); 160 // printf("%d\n",key); 161 return 0; 162 }