codevs1907 方格取数 3

  

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

  虽然想了很久,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 }
时间: 2024-12-24 21:04:05

codevs1907 方格取数 3的相关文章

【codevs1907】方格取数3(最大流最小割定理)

网址:http://codevs.cn/problem/1907/ 题意:在一个矩阵里选不相邻的若干个数,使这些数的和最大. 我们可以把它看成一个最小割,答案就是矩阵中的所有数-最小割.先把矩阵按国际象棋棋盘黑白染色(即把相邻的点分别染成白色和黑色),然后黑点连源点,白点连汇点.割掉一个点到源/汇的边就是不选择这个点,最后的目的就是使源到汇不连通(不引发题目不能选择位置相邻的数的矛盾). 然而最小割怎么求呢? 于是我们就要引入一个定理:最大流最小割定理.它的意思就是说,在一个图中,a点到b点的最

线性规划与网络流24题●09方格取数问题&amp;13星际转移问题

●(做codevs1908时,发现测试数据也涵盖了1907,想要一并做了,但因为"技术"不佳,搞了一上午) ●09方格取数问题(codevs1907  方格取数3) 想了半天,也没成功建好图: 无奈下参考题解,说是本题要求二分图点权最大独立集,然后可以由结论:"最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 - 网络最大流"转化到求最大流(我真的很懵逼,但又感觉很有道理): 下面附上solution:(自己领悟吧) (不懂

hdu 1565 方格取数(2)(网络流之最大点权独立集)

题目链接:hdu 1565 方格取数(2) 题意: 有一个n*m的方格,每个方格有一个数,现在让你选一些数.使得和最大. 选的数不能有相邻的. 题解: 我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有: 最大点权独立集 + 最小点权覆盖集 = 总点权和, 这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图, 1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一

P1004 方格取数

P1004 方格取数 题目描述 设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放 人数字0.如下图所示(见样例): A 0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 0 0 0 0 0 7 0 0 0 0 0 0 14 0 0 0 0 0 21 0 0 0 4 0 0 0 0 15 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . B 某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角

hdoj 1569 方格取数(2) 【最小割】 【最大点权独立集】

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5589    Accepted Submission(s): 1741 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的

1475: 方格取数

1475: 方格取数 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 578  Solved: 309[Submit][Status][Discuss] Description 在一个n*n的方格里,每个格子里都有一个正整数.从中取出若干数,使得任意两个取出的数所在格子没有公共边,且取出的数的总和尽量大. Input 第一行一个数n:(n<=30) 接下来n行每行n个数描述一个方阵 Output 仅一个数,即最大和 Sample Input 2 1 2

hdu 1569 方格取数(2) 网络流 最大点权独立集

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5146    Accepted Submission(s): 1610 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的

HDU 1565 方格取数(1) (状态压缩 DP)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5779    Accepted Submission(s): 2194 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出

HDU 1565 方格取数(1)(状压dp)

感觉这道题目的数据比较水啊,程序的时间复杂度为1711^2*20竟然也可以过掉....其他的就是状压了啊,注意需要滚动一下啊.... 方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5701    Accepted Submission(s): 2159 Problem Description 给你一个n*n的格子的棋