【算法竞赛进阶指南】CH5103 NOIP2008 T3 传纸条 线性dp

Description

给定一个 N*M 的矩阵A,每个格子中有一个整数。现在需要找到两条从左上角 (1,1) 到右下角 (N,M) 的路径,路径上的每一步只能向右或向下走。路径经过的格子中的数会被取走。两条路径不能经过同一个格子。求取得的数之和最大是多少。N,M≤50。

数据规模约定:
30%的数据满足:1<=m,n<=10
100%的数据满足:1<=m,n<=50

Input

第一行有2个用空格隔开的整数n和m,表示有n行m列(1<=n,m<=50)。
接下来的n行是一个n*m的矩阵,每行的n个整数之间用空格隔开。

Output

一个整数,表示答案。

Sample Input

3 3
0 3 9
2 8 5
5 7 0

Sample Output

34

状态:路径长度/当前走过的步数。
记当前的步数为i,当前的两个坐标分别为x1,y1、x2,y2。我们可以只用3个变量i,x1,x2,F[i,x1,x2] 代表当前状态(y1 = i + 2 - x1,y2 = i + 2 - x2)。
起始状态:F[0,1,1] = A[1,1]
目标状态:F[N+M-2,N,N] = A[N,N]


#include 
#include 
using namespace std;
const int maxn = 56;
int n, m, g[maxn][maxn], f[maxn << 1][maxn][maxn];

void dp()
{
	for (int i = 0; i < n + m - 2; i++)
		// j是第一条路径上点的横坐标x1
		for (int j = 1; j <= n && j <= i + 1; j++)
			// k是第二条路径上点的横坐标x2
			for (int k = 1; k <= n && k <= i + 1; k++)
			{
				// 1. 下次都向一个方向扩展

				// 当前两个在一个格子 -> 两条路径可以扩展到同一个格子
				if (j == k)
				{
					// 下一次移动两个都向右走,都进入同一个格子g[x1][y1],其中y1=(i+1)+2-x1
					f[i + 1][j][k] = max(f[i + 1][j][k], f[i][j][k] + g[j][i + 3 - j]);
					// 下一次移动两个都向下走,都进入同一个格子g[x1][y1],其中y1=(i+1)+2-x1
					f[i + 1][j + 1][k + 1] = max(f[i + 1][j + 1][k + 1], f[i][j][k] + g[j + 1][i + 2 - j]);
				}

				// 当前两个不在一个格子 -> 不能扩展到同一个格子
				else
				{
					// 下一次移动两个都向右走
					f[i + 1][j][k] = max(f[i + 1][j][k], f[i][j][k] + g[j][i + 3 - j] + g[k][i + 3 - k]);
					// 下一次移动两个都向下走
					f[i + 1][j + 1][k + 1] = max(f[i + 1][j + 1][k + 1], f[i][j][k] + g[j + 1][i + 2 - j] + g[k + 1][i + 2 - k]);
				}


				// 2. 下次1号点向下,2号点向右

				// 1号点在2号点右上方,此时1号点向下,2号点向右 -> 两条路径可以扩展到同一个格子
				if (j + 1 == k)
					f[i + 1][j + 1][k] = max(f[i + 1][j + 1][k], f[i][j][k] + g[j + 1][i + 2 - j]);
				// 1号点向下,2号点向右 -> 不能扩展到同一个格子
				else
					f[i + 1][j + 1][k] = max(f[i + 1][j + 1][k], f[i][j][k] + g[j + 1][i + 2 - j] + g[k][i + 3 - k]);

				// 3. 下次1号点向右,2号点向下 , 同上
				if (k + 1 == j)
					f[i + 1][j][k + 1] = max(f[i + 1][j][k + 1], f[i][j][k] + g[j][i + 3 - j]);
				else
					f[i + 1][j][k + 1] = max(f[i + 1][j][k + 1], f[i][j][k] + g[j][i + 3 - j] + g[k + 1][i + 2 - k]);
			}
}

int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			cin >> g[i][j];
	dp();
	cout << f[n + m - 2][n][n] << endl;
	return 0;
}

你可能感兴趣的:(动态规划)