方格取数<luogu1004>

题目见洛谷:https://www.luogu.org/problem/show?pid=1004
  一个人走两遍,从左上到右下,可以看做是两个人同时从左上到右下,动态规划可以理解为有策略的递推,递推则是从已知推向未知。从题意中可以知道,当如果只有1*1时是已知的,当站在某个(x,y)格子里时,它的最优值是来自于左边或者上边的最大值+(x,y)点上的数。那么当有两个人同时从左上到右下时,则有四种情况:
1. 上 , 上
2. 上 , 左
3. 左 , 上
4. 左 , 左
  因此需要在这四种情况中最大的+ 这俩人所站的当前位置上的两个数。特殊的,当两个人走到了同一个格子里时,因为当一个人取走后便为0了,因此只加一次。所以可以用四维数组记录两个人走到对应格子里时的最优值,dp[i][j][x][y]代表一个人站在(i,j)格子里,另一个人站在(x,y)格子里时的最优值,动态规划转移方程是:假设第一个人站在(i,j)格子,第二个人站在(x, y)格子里。那么动态规划转移方程为:
  当俩人未站在同一个点时:
  dp[i][j][x][y]=map[x][y]+map[i][j]+max(max(dp[i][j-1][x-1][y],dp[i-1][j][x][y-1]),max(dp[i-1][j][x-1][y],dp[i][j-1][x][y-1]));
其中{1<= i,j,x,y <=n;}
  当俩人走到了同一个格子里时:
  dp[i][j][x][y] = map[i][j]+max(max(dp[i][j-1][x-1][y],dp[i-1][j][x][y-1]),max(dp[i-1][j][x-1][y],dp[i][j-1][x][y-1]));其中{1<= i,j,x,y <=n;}
  代码如下:

#include<iostream>
#include<algorithm>
using namespace std;

int dp[10][10][10][10],map[10][10];
int main(){
    int n;
    cin >> n;
    while(1){
        int x,y,num;
        cin >> x >> y >> num;
        if(x ==0 && y==0 && num==0)
            break;
        map[x][y] = num;
    }
    dp[1][1][1][1] = map[1][1];
    for(int i = 1; i<= n; i++)
        for(int j = 1; j<= n; j++)
            for(int x = 1; x<= n; x ++)
                for(int y = 1; y <= n; y++){
                    if(i == x && j == y){
                        dp[i][j][x][y] = map[i][j]+max(max(dp[i][j-1][x-1][y],dp[i-1][j][x][y-1]),max(dp[i-1][j][x-1][y],dp[i][j-1][x][y-1]));
                    }
                    else{
                        dp[i][j][x][y] = map[x][y]+map[i][j]+max(max(dp[i][j-1][x-1][y],dp[i-1][j][x][y-1]),max(dp[i-1][j][x-1][y],dp[i][j-1][x][y-1]));
                    }
                }
    cout << dp[n][n][n][n] << endl;
    return 0;
}

  由于在方格中取数第k次取数所站的格子一定满足 行号+ 列号 = k+1,那么当知道俩人所在的格子行号的情况下,根据k便可算的对应的列号。因此我们可以将上面的动态规划转变成三维,减少空间复杂度。
  具体代码见:

#include<iostream>
#include<algorithm>
using namespace std;

int dp[21][10][10],map[10][10];
int main()
{
    int n;
    cin >> n;
    while(1)
    {
        int x,y,num;
        cin >> x >> y >> num;
        if(x ==0 && y==0 && num==0)
            break;
        map[x][y] = num;
    }
    dp[1][1][1] = map[1][1];
    for(int k= 2; k<= 2*n-1 ;k++)
        for(int i = 1; i<= n; i++)
            for(int x = 1; x<= n; x++)
                {
                    int j= k+1-i,y = k+1 - x;
                    if(i == x )
                    {
                        dp[k][i][x] = map[i][j]+max(max(dp[k-1][i-1][x-1],dp[k-1][i][x]),max(dp[k-1][i-1][x],dp[k-1][i][x-1]));
                    }
                    else
                    {
                        dp[k][i][x] = map[x][y]+map[i][j]+max(max(dp[k-1][i-1][x-1],dp[k-1][i][x]),max(dp[k-1][i-1][x],dp[k-1][i][x-1]));
                    }
                }
    cout << dp[2*n-1][n][n] << endl;
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值