码迷,mamicode.com
首页 > 其他好文 > 详细

codevs1907 方格取数 3

时间:2016-06-01 23:06:08      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:

  技术分享

  嗯......这道题大概算是自己想出来的第一道网络流的题吧?

  虽然想了很久,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 }

codevs1907 方格取数 3

标签:

原文地址:http://www.cnblogs.com/lcf-2000/p/5551356.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!