51nod 更难的矩阵取数问题 + 滚动数组优化

这里要求要走到终点再走回来,可以转化为两个人走。

那么我们可以先粗暴的设f[x1][y1][x2][y2]为第一个人走到(x1, y1), 第二个人走到(x2, y2)的最大价值。

那么这样空间会很大,通过观察可以发现,一个走的步数=横坐标+纵坐标,因为走一步一定是横坐标

或者纵坐标+1.

那么我们就可以转化为f[step][x1][x2],可以退出y1 = step - x1, y2 = step - x2

那么转移方程就很好求了

f[step][x1][x2] = max(f[step-1][x1-1][x2], f[step-1][x1-1][x2-1], f[step-1][x1][x2-1], f[step-1][x1][x2]) + a[i][step-i]

+ (i == j ? 0 : a[j][step-j])

这里要判断如果是同一个格子的话只加一次。

实际这样已经可以过了,代码如下

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 212;
int f[MAXN*2][MAXN][MAXN];
int a[MAXN][MAXN], n, m;

void up(int& x, int a) { x = max(x, a); }

int main()
{
	scanf("%d%d", &m, &n);
	REP(i, 1, n + 1)
		REP(j, 1, m + 1)
			scanf("%d", &a[i][j]);
	REP(k, 2, n + m + 1)
	{
		for(int i = 1; i <= n && i + 1 <= k; i++)
			for(int j = 1; j <= n && j + 1 <= k; j++)
			{
				for(int r1 = -1; r1 <= 0; r1++)
					for(int r2 = -1; r2 <= 0; r2++)
						up(f[k][i][j], f[k - 1][i + r1][j + r2]);
				f[k][i][j] += a[i][k-i] + (i == j ? 0 : a[j][k-j]);
			}
	}
	printf("%d\n", f[n + m][n][n]);
	return 0;	
} 

但是呢其实空间上还可以更优化,因为只和k-1有关

那么我这里想到两种方法实现滚动数组。

第一个就是开两个二维数组,然后就来回更新。

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 212;
int f[2][MAXN][MAXN];
int a[MAXN][MAXN], n, m;

void up(int& x, int a) { x = max(x, a); }

int main()
{
	scanf("%d%d", &m, &n);
	REP(i, 1, n + 1)
		REP(j, 1, m + 1)
			scanf("%d", &a[i][j]);
	int t = 0;
	REP(k, 2, n + m + 1)
	{
		for(int i = 1; i <= n && i + 1 <= k; i++)
			for(int j = 1; j <= n && j + 1 <= k; j++)
			{
				for(int r1 = -1; r1 <= 0; r1++)
					for(int r2 = -1; r2 <= 0; r2++)
						up(f[t][i][j], f[t ^ 1][i + r1][j + r2]);
				f[t][i][j] += a[i][k-i] + (i == j ? 0 : a[j][k-j]);
			}
		t ^= 1;	
	}
	printf("%d\n", f[t ^ 1][n][n]);
	return 0;	
} 

还一种有点类似01背包逆推那个做法,只用一个数组就可以实现,改变循环顺序就好了。

为了让当前状态转移的时候,用来更新的值都是上一行的,所以我们要逆序来操作。

因为如果是从上到下,从左到右的话,要更新当前状态,需要用到f[i-1][j]等,而这个时候

f[i-1][j]之前已经算过了,已经更新过了,只要更新过了就成了这一行的值了,就不行。

所以我们要让f[i-1][j], f[i-1][j-1], f[i][j-1],f[i][j]都没有更新过。

所以我们就从下到上,从右到左来推,这样就可以保证都是上一行的值了。

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 212;
int f[MAXN][MAXN];
int a[MAXN][MAXN], n, m;

void up(int& x, int a) { x = max(x, a); }

int main()
{
	scanf("%d%d", &m, &n);
	REP(i, 1, n + 1)
		REP(j, 1, m + 1)
			scanf("%d", &a[i][j]);
	REP(k, 2, n + m + 1)
	{
		for(int i = min(n, k - 1); i >= 1; i--)
			for(int j = min(n, k - 1); j >= 1; j--)
			{
				for(int r1 = -1; r1 <= 0; r1++)
					for(int r2 = -1; r2 <= 0; r2++)
						up(f[i][j], f[i + r1][j + r2]);
				f[i][j] += a[i][k-i] + (i == j ? 0 : a[j][k-j]);
			}
	}
	printf("%d\n", f[n][n]);
	return 0;	
} 

 

posted @ 2018-08-18 15:15  Sugewud  阅读(151)  评论(0编辑  收藏  举报