传纸条(nyoj 61)

nyoj 61:点击打开链接

找两条从左上角到右下角不相交的路径(除了头和尾),使路径上的和最大.

首先是四维的数组,比较好理解。

f[x1][y1][x2][y2]=max{f[x1-1][y1][x2-1][y2],f[x1-1][y1][x2][y2-1],f[x1][y1-1][x2-1][y2],f[x1][y1-1][x2][y2-1]}+map[x1][y1]+map[x2][y2] ;(其中x1 !=x2 || y1 !=y2)

其中f[x1][y1][x2][y2]代表当前第一条路径经过点(x1, y1),第二条路径经过点(x2, y2),注意这两点不能重合。

每个路径都能从左边和上面走过来,所以在四种情况中选一个最大的。但对于这一题来说会超时。

进一步的将四位数组改成三维。

因为每个时刻x1 + y1 == x2 + y2,也就是所走的步数都是一样的,所以,就可以将y1, y2去掉然后在增添一个k值表示所走的横纵坐标之和。

f[k][x1][x2] 这样y1 = k - x1, y2 = k - x2,状态转移方程为max(f[k - 1][x1 - 1][x2], f[k - 1][x1 - 1][x2 - 1], f[k - 1][x1][x2], f[k - 1][x1][x2 - 1]) + map[x1][k - x1] + map[x2][k - x2];

知道了状态转移方程但是写起来还是有很多需要注意的地方……

注意solve()中第三个for循环,jshi从i+1开始的。j从i+1开始可以避免j==i,也可以保证第一条路径一定选取在第二条路径的上方,如果j也从1开始循环,那么会有重复的部分,因为第一条路径在第二条路径的上方和第一条路径在第二条路径的下方其实是重复的,总的来说两种情况所得的结果是一样的。

#include <stdio.h>
#include <string.h>
int a[100][100];
int dp[110][110][110];
int m, n;
inline int max(int c, int d, int e, int f)
{
	int m = c;
	if(m < d)
		m = d;
	if(m < e)
		m = e;
	if(m < f)
		m = f;
	return m;
}

int solve()
{
	int i, j, k, c, d;
	for(k = 2; k < m + n; k ++)
	{//枚举所走的横纵坐标之和 
		c = m > k ? k : m;//横坐标不可能大于横纵坐标之和,也不可能大于横坐标的边界 
		for(i = 1; i <= c; i++)
		{//枚举第一条路径的横坐标 
			for(j = i + 1; j <= c; j++)
			{//第二条路径的横坐标
				dp[k][i][j] = max(dp[k - 1][i][j], dp[k - 1][i - 1][j], dp[k - 1][i][j - 1], dp[k - 1][i - 1][j - 1]);
				dp[k][i][j] += a[i][k - i] + a[j][k - j];
			}
		}
	}
	//注意返回的不是dp[m + n][m][m],也不是dp[m +n -1][m][m - 1],因为dp[m +n][m][m]根本走不到  
	//i的值一定比j小,所以dp[m +n- 1][m][m - 1]的值是0 
	return dp[m + n - 1][m - 1][m];
}

int main (void)
{
	int t;
	scanf("%d", &t);
	while(t --)
	{
		memset(dp, 0, sizeof(dp));
		int i, j;
		scanf("%d %d", &m, &n);
		for(i = 1; i <= m; i++)
			for(j = 1; j <= n; j++)
				scanf("%d", &a[i][j]);
		//如果map[m][n]也是有非0数值的,就要在单独加上 
		printf("%d\n", solve());
	}	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值