嗯......这道题大概算是自己想出来的第一道网络流的题吧?
虽然想了很久,WA了很多发,但终于A掉了......
网络流的题真是难想,如果不是我已经知道这道题要用网络流做,还不知道要想到什么时候去了......
好了,不扯多了,进正题:
首先,我们发现直接建模的话非常不好搞,体重的条件不好表示......
于是,我们就想,是否可以把我们选完书之后剩下的数给表示出来呢?我们发现这个不难做到。只需将棋盘黑白二染色,把黑点、白点各看成一块,相邻的格子间有边相连,不难发现将黑白两块分开的割的方案就是不选的点的合法方案。所以最小割即是合法方案中选出的点和最大的方案。于是我们可以从源点向所有黑(白)点连一条容量为这个格子里的数的边,从黑(白)点向相邻的点连一条容量为INF的边,再从白(黑)点向汇点连一条容量为当前格子里的数的边,跑一边最大流即可得出不选的点的最小和,用所有数字之和减去它就是答案。
下面贴代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define maxm 100010 7 #define INF (1<<25) 8 #define r(j) (j^1) 9 10 using namespace std; 11 typedef long long llg; 12 13 int head[101*101],next[maxm],to[maxm],c[maxm],tt=1; 14 int a[101][101],zx[4]={0,0,1,-1},zy[4]={1,-1,0,0}; 15 int d[maxm],l,r,dep[maxm],ans,tut,s,t,n,m; 16 17 int getint(){ 18 int w=0;bool q=0; 19 char c=getchar(); 20 while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar(); 21 if(c==‘-‘) q=1,c=getchar(); 22 while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); 23 return q?-w:w; 24 } 25 26 void link(int x,int y,int z){ 27 to[++tt]=y;next[tt]=head[x];head[x]=tt; 28 to[++tt]=x;next[tt]=head[y];head[y]=tt; 29 c[tt^1]=z; 30 } 31 32 bool bfs(){ 33 for(int i=1;i<=t;i++) dep[i]=0; 34 l=r=0;d[r++]=s;dep[s]=1;int u; 35 while(l!=r){ 36 u=d[l++]; 37 for(int i=head[u];i;i=next[i]) 38 if(!dep[to[i]] && c[i]>0){ 39 dep[to[i]]=dep[u]+1; 40 d[r++]=to[i]; 41 } 42 } 43 return dep[t]>0; 44 } 45 46 int dfs(int u,int low){ 47 int res=0,v; 48 if(u==t) return low; 49 if(!low) return 0; 50 for(int i=head[u];i;i=next[i]) 51 if(c[i]>0 && dep[to[i]]==dep[u]+1){ 52 v=dfs(to[i],min(low-res,c[i])); 53 c[i]-=v;c[r(i)]+=v;res+=v; 54 } 55 return res; 56 } 57 58 int main(){ 59 freopen("a.in","r",stdin); 60 freopen("a.out","w",stdout); 61 n=getint();m=getint();s=n*m+1;t=s+1; 62 for(int i=1;i<=n;i++) 63 for(int j=1;j<=m;j++) 64 a[i][j]=getint(); 65 for(int i=1,now(0);i<=n;i++) 66 for(int j=1;j<=m;j++){ 67 now++; 68 if(!((i+j)&1)){ 69 link(s,now,a[i][j]); 70 for(int k=0,x,y,n1;k<4;k++){ 71 x=i+zx[k];y=j+zy[k]; 72 if(x>0 && x<=n && y>0 && y<=m){ 73 n1=(x-1)*m+y; 74 link(now,n1,INF); 75 } 76 } 77 } 78 else link(now,t,a[i][j]); 79 tut+=a[i][j]; 80 } 81 while(bfs()) 82 while(int tot=dfs(s,INF)) ans+=tot; 83 printf("%d\n",tut-ans); 84 return 0; 85 }
---恢复内容结束---
嗯......这道题大概算是自己想出来的第一道网络流的题吧?
虽然想了很久,WA了很多发,但终于A掉了......
网络流的题真是难想,如果不是我已经知道这道题要用网络流做,还不知道要想到什么时候去了......
好了,不扯多了,进正题:
首先,我们发现直接建模的话非常不好搞,体重的条件不好表示......
于是,我们就想,是否可以把我们选完书之后剩下的数给表示出来呢?我们发现这个不难做到。只需将棋盘黑白二染色,把黑点、白点各看成一块,相邻的格子间有边相连,不难发现将黑白两块分开的割的方案就是不选的点的合法方案。所以最小割即是合法方案中选出的点和最大的方案。于是我们可以从源点向所有黑(白)点连一条容量为这个格子里的数的边,从黑(白)点向相邻的点连一条容量为INF的边,再从白(黑)点向汇点连一条容量为当前格子里的数的边,跑一边最大流即可得出不选的点的最小和,用所有数字之和减去它就是答案。
下面贴代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define maxm 100010 7 #define INF (1<<25) 8 #define r(j) (j^1) 9 10 using namespace std; 11 typedef long long llg; 12 13 int head[101*101],next[maxm],to[maxm],c[maxm],tt=1; 14 int a[101][101],zx[4]={0,0,1,-1},zy[4]={1,-1,0,0}; 15 int d[maxm],l,r,dep[maxm],ans,tut,s,t,n,m; 16 17 int getint(){ 18 int w=0;bool q=0; 19 char c=getchar(); 20 while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar(); 21 if(c==‘-‘) q=1,c=getchar(); 22 while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); 23 return q?-w:w; 24 } 25 26 void link(int x,int y,int z){ 27 to[++tt]=y;next[tt]=head[x];head[x]=tt; 28 to[++tt]=x;next[tt]=head[y];head[y]=tt; 29 c[tt^1]=z; 30 } 31 32 bool bfs(){ 33 for(int i=1;i<=t;i++) dep[i]=0; 34 l=r=0;d[r++]=s;dep[s]=1;int u; 35 while(l!=r){ 36 u=d[l++]; 37 for(int i=head[u];i;i=next[i]) 38 if(!dep[to[i]] && c[i]>0){ 39 dep[to[i]]=dep[u]+1; 40 d[r++]=to[i]; 41 } 42 } 43 return dep[t]>0; 44 } 45 46 int dfs(int u,int low){ 47 int res=0,v; 48 if(u==t) return low; 49 if(!low) return 0; 50 for(int i=head[u];i;i=next[i]) 51 if(c[i]>0 && dep[to[i]]==dep[u]+1){ 52 v=dfs(to[i],min(low-res,c[i])); 53 c[i]-=v;c[r(i)]+=v;res+=v; 54 } 55 return res; 56 } 57 58 int main(){ 59 freopen("a.in","r",stdin); 60 freopen("a.out","w",stdout); 61 n=getint();m=getint();s=n*m+1;t=s+1; 62 for(int i=1;i<=n;i++) 63 for(int j=1;j<=m;j++) 64 a[i][j]=getint(); 65 for(int i=1,now(0);i<=n;i++) 66 for(int j=1;j<=m;j++){ 67 now++; 68 if(!((i+j)&1)){ 69 link(s,now,a[i][j]); 70 for(int k=0,x,y,n1;k<4;k++){ 71 x=i+zx[k];y=j+zy[k]; 72 if(x>0 && x<=n && y>0 && y<=m){ 73 n1=(x-1)*m+y; 74 link(now,n1,INF); 75 } 76 } 77 } 78 else link(now,t,a[i][j]); 79 tut+=a[i][j]; 80 } 81 while(bfs()) 82 while(int tot=dfs(s,INF)) ans+=tot; 83 printf("%d\n",tut-ans); 84 return 0; 85 }