【codevs 1902】方格取数3(最小割)

1907 方格取数 3
时间限制: 2 s   空间限制: 256000 KB    题目等级 : 大师 Master
题目描述 Description
 
在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
   
«编程任务:对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入描述 Input Description
   
第1 行有2 个正整数m和n,分别表示棋盘的行数和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。
输出描述 Output Description
  
将取数的最大总和输出
样例输入 Sample Input
   3 3
   1
 2 3
   
3 2 3
   
2 3 1
样例输出 Sample Output
  
11
数据范围及提示 Data Size & Hint

  n,m<=30


【题解】【网络流最小割】

【黑白染色,将相邻两个格染上不同的颜色,将源点与白点连边,黑点与汇点连边,流量为当前格中的值。再将相邻的点连边,流量为极大值。然后跑最小割(注:最小割=最大流),故直接跑最大流,最后用所有数的和减去最大流】 

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lng 10000000
using namespace std;
int a[10010],next[10010],p[10010],remain[10010],tot;
int dis[10010],cur[10010];
int n,m,ans,sum,n1,cnt,as;

inline void add(int x,int y,int z)
{
	tot++; a[tot]=y; next[tot]=p[x]; p[x]=tot; remain[tot]=z;
	tot++; a[tot]=x; next[tot]=p[y]; p[y]=tot; remain[tot]=0;
	return;
}

inline bool bfs()
{
	memset(dis,-1,sizeof(dis));
	queue<int>que;
	for (int i=0;i<=n1;++i)
	 cur[i]=p[i];
	 que.push(0); dis[0]=0;
	while (!que.empty())
	 {
	 	int u,v;
	    u=que.front(); que.pop();
	    v=p[u];
	    while (v!=-1) 
	     {
	     	if (remain[v]&&dis[a[v]]<0)
	     	 {
	     	 	dis[a[v]]=dis[u]+1;
	     	 	que.push(a[v]);
	     	 }
	     	v=next[v];
	     }
	 }
	if (dis[n1]<0) return false;
	  else return true;
}
inline int dfs(int now,int flow)
{
	if (now==n1||!flow) return flow;
	int u=cur[now],s;
	while (u!=-1)
	 {
	 	cur[now]=u;
		if (dis[a[u]]>0&&dis[a[u]]==dis[now]+1&&(s=dfs(a[u],min(flow,remain[u]))))
	 	 {
	 	 	remain[u]-=s; remain[u^1]+=s;
	 	    return s;
	 	 }
	 	u=next[u];
	 }
	return 0;
}
int main()
{
	int i,j;
	memset(p,-1,sizeof(p));
	memset(next,-1,sizeof(next));
	scanf("%d%d",&n,&m);
	n1=n*m+1;
	as=ans=sum=cnt=0; tot=-1;
	for (i=1;i<=n;++i)
	 for (j=1;j<=m;++j)
	  {
	  	int x;
	  	cnt++;
	  	scanf("%d",&x);
	  	ans+=x;
	  	if (i%2==j%2)
	  	 {
	  	 	add(0,cnt,x);
			if (i==1) add(cnt,cnt+m,lng);
	  	 	if (i==n) add(cnt,cnt-m,lng);
	  	 	if (i>1&&i<n) {add(cnt,cnt+m,lng); add(cnt,cnt-m,lng);}
	  	 	if (j==1) add(cnt,cnt+1,lng);
	  	 	if (j==m) add(cnt,cnt-1,lng);
	  	 	if (j>1&&j<m) {add(cnt,cnt+1,lng); add(cnt,cnt-1,lng);}
	  	 }
	  	else add(cnt,n1,x); 	
	  }
	while (bfs())
	  while (as=dfs(0,0x7fffffff))
	    sum+=as;
    ans-=sum;
    printf("%d\n",ans);
    return 0;
}


方格问题是经典的动态规划问题之一,在这个问题中,我们通常有一个矩阵(二维组),每个元素代表该位置可以获的价值。任务是从左上角走到右下角,每次只能向右走或向下走,并希望最大化路径上的总价值。 ### C++ 实现思路 #### 动态规划表 (dp table) 我们可以创建一个同样大小的 `dp` 表来存储到达每一个点的最大值。初始条件是起点即 dp[0][0] = 矩阵起始点值。对于其他位置 `(i, j)` 的最大值则决于其左边和上面两个方向过来的结果: ```cpp // 初始化第一行 和 第一列 因为它们只有唯一一条路径可达 for(int i = 1; i < n; ++i) { grid[i][0] += grid[i-1][0]; // 更新第i行第0列的位置值 } for(int j = 1; j < m; ++j){ grid[0][j] += grid[0][j-1]; // 更新第0行第j列的位置值 } // 构建DP Table for(int i = 1 ; i<n;i++){ for(int j=1;j<m;j++) { grid[i][j]+= max(grid[i - 1][j],grid[i][j - 1]); } } ``` 最后返回最右边底端单元格的值即可获得从开始到结束所经过的所有字之和的最大化结果。 下面是完整的程序示例: ```cpp #include<bits/stdc++.h> using namespace std; int main() { int n,m; cin >> n >> m; vector<vector<int>> grid(n,vector<int>(m)); // 输入据 for(int i=0;i<n;++i){ for(int j=0;j<m;++j){ cin >> grid[i][j]; } } if (!n || !m) return 0; // 计算累积和 for(int i=1;i<n;i++) grid[i][0]=grid[i-1][0]+grid[i][0]; for(int j=1;j<m;j++) grid[0][j]=grid[0][j-1]+grid[0][j]; for(int i=1;i<n;i++) for(int j=1;j<m;j++) grid[i][j]=max(grid[i-1][j],grid[i][j-1])+grid[i][j]; cout << "最大收集的量:" << grid[n-1][m-1]<<endl; return 0; } ``` 此段代码实现了读入矩形区域的据、计算并打印出由(0,0)至终点所能得到的最大累计得分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值