NYOJ 传纸条(一) 双向dp

刚开始的想法是跑两遍dp  第一次找权值最大的路径  然后把走过的路径标记为负无穷大  然后在走一遍就好了  但是后来发现如果要让两次的和最大  那么第一次的路线可能不是最大的一条路  所以并不能这么写   看了别人的题解  题解我都看了两天 真是有意思


思路是这样的  我们假设有两个小人从左上角走到右下角  不同路线走 不相交  就代替了这个模型  用一个dp[i][j][k] 来表示当走了I步时其中一个人的x坐标为j另一个人的x坐标为k

不难想到当前状态可以来自四种其他状态  分别是小人一号来自左边 小人二号来自上边 或者小人一号来自左边小人二号来自左边 或者小人一号来自上边小人二号来自左边 或者小人一号来自上边小人二号来自上边  就这样  转移方程是 :dp[i][j][k] = max(max(dp[I - 1][j][k], dp[I - 1][j - 1][k]), max(dp[I - 1][j - 1][k - 1], dp[I - 1][j][k - 1]));


我写这道题的时候把储存权值的表定义成了char类型 然后debug一早上 真是醉了

参考了这个人的博客 写的很详细

点击打开链接


#include 
#include 
#include 
using namespace std;
int dp[110][55][55];
int main()
{		
	int t, n, m;
	int  str[55][55];
	cin >> t ;
	while ( t -- ) {
		cin >> n >> m ;
		for (int i = 1;i <= n; i ++) 
			for (int j = 1; j <= m; j ++)
				cin >> str[i][j] ;
		memset(dp , 0, sizeof(dp));
		for (int i = 1; i < m + n ; i ++) {
			for (int j = 1; j <= min(n , i); j ++) {
				int y1 = i - j + 1;
				if(y1 > m)continue;
				for (int k = j +1; k <=min(i , n); k ++) {
					int y2 = i - k + 1;
					int temp = -1;
					temp = max(temp , dp[i - 1][j - 1][k - 1]);
					temp = max(temp , dp[i - 1][j - 1][k]);
					temp = max(temp , dp[i - 1][j][k - 1]);
					temp = max(temp , dp[i - 1][j][k]);
					dp[i][j][k] = temp + str[j][y1] + str[k][y2];
				}
			}
		}
		int ans = 0;
		ans = max (ans , dp[m + n - 2][n - 1][n]);
		ans = max (ans , dp[m + n - 2][n][n - 1]);
		cout << ans << endl;
	}
}

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