您的位置:首页 > 其它

51nod 1084 更难的矩阵取数问题(DP)

2015-10-04 22:06 274 查看
原题链接:点击打开链接

一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。

 
例如:3 * 3的方格。

1 3 3
2 1 3
2 2 1

能够获得的最大价值为:17。1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -> 2 -> 1。其中起点和终点的奖励只计算1次。

输入

第1行:2个数M N,中间用空格分隔,为矩阵的大小。(2 <= M, N <= 200)
第2 - N + 1行:每行M个数,中间用空格隔开,对应格子中奖励的价值。(1 <= A[i,j] <= 10000)


输出

输出能够获得的最大价值。


输入示例

3 3
1 3 3
2 1 3
2 2 1


输出示例

17


比较容易想到的是直接进行两次类似数塔问题的DP。实际上,这个解法是有问题的。

比如:

5 5

10   1   1   1    1

10   1   1   1    1

10 10 10   1  10

  1   1   1   1    1

10   1 10  10 10

其实,可以将两次取数看作两个人同时从左上角出发(只能向右/向下走),由此设出状态:

dp[x1][y1][x2][y2]表示第一个人走到(x1,y1),第二个人走到(x2,y2)时的最大价值。

那么有:dp[x1][y1][x2][y2]=max(dp[x1'][y1'][x2'][y2'])+M[x1][y1]+M[x2][y2]。 (x1',y1'为向右或向左走一步能够到达x1、y1的格子)

时间和空间复杂度均为O(n^2*m^2)

优化:

发现两人每走一步都有:x1+y1=x2+y2=当前走的步数

如果知道走的步数,只要知道某人的横坐标即可推算出其纵坐标。因此设dp[i][j][k]表示两人走i步,第一个人到达第i行,第二个人到达第k行时的最大价值。

dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-1][k],dp[i-1][j][k-1],dp[i-1][j-1][k-1])+M[x1][y1]+M[x2][y2] (x1!=x2)

时间和空间复杂度优化到O((n+m)*n^2)

注意到,状态转移方程中dp[i][j][k]的状态只与前一步的状态有关,因此空间复杂度还可以优化到O(n^2)

上面的状态转移方程为x1!=x2时的,由于当两人走过同一个格子时,该格子价值只算一次,因此当x1=x2时,加上M[x1][y1]即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 201
int M[maxn][maxn];
int dp[maxn<<1][maxn][maxn];
int main()
{
int n,m,i,j,x1,x2;
scanf("%d%d",&m,&n);
for(i=0;i<n;++i)
for(j=0;j<m;++j)
scanf("%d",&M[i][j]);
memset(dp,0,sizeof(dp));
dp[0][0][0]=M[0][0];
for(i=1;i<n+m;++i)
for(x1=0;x1<n&&i-x1>=0;++x1)
for(x2=0;x2<n&&i-x2>=0;++x2)
{
if(x1-1>=0&&x2-1>=0)
dp[i][x1][x2]=max(dp[i][x1][x2],dp[i-1][x1-1][x2-1]+M[x1][i-x1]+(x1==x2?0:M[x2][i-x2]));
if(x1-1>=0&&i-x2-1>=0)
dp[i][x1][x2]=max(dp[i][x1][x2],dp[i-1][x1-1][x2]+M[x1][i-x1]+(x1==x2?0:M[x2][i-x2]));
if(x2-1>=0&&i-x1-1>=0)
dp[i][x1][x2]=max(dp[i][x1][x2],dp[i-1][x1][x2-1]+M[x1][i-x1]+(x1==x2?0:M[x2][i-x2]));
if(i-x2-1>=0&&i-x1-1>=0)
dp[i][x1][x2]=max(dp[i][x1][x2],dp[i-1][x1][x2]+M[x1][i-x1]+(x1==x2?0:M[x2][i-x2]));
}
printf("%d\n",dp[n+m-1][n-1][n-1]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: