【洛谷】P1004 方格取数

95 阅读2分钟

本文大纲

题目大意

题目链接:P1004 方格取数

有一个N * N(1≤N≤9)的方阵,其中有数字,数字可以取走,取走后变成0。有一人希望从左上角走到右下角(仅能向右或向下),再从右下角走到左上角(仅能向上或向左)。希望取走的数的总和最大。

题目解析

这道题是一道动态规划题目,如果还不知道什么是动态规划,那么请看我的动态规划学习笔记。我们按照动态规划五部曲来解这到题目。

定义状态

这道题需要定义一来一回两条路,或者说两条去的路。每一个位置需要用两维表示(x,y),所以一共需要四维数组来表示,这道题数据比较小,1≤N≤9,一共只需要94 ,也就是6561,不会超出内存限制。f[i][j][k][l] = 第一条路线从(1,1)到(i,j)的最大价值+第二条路线从(1,1)到(k,l)的最大价值。

确认状态转移方程

这道题的状态转移方程很简单,因为只能向下或向右,而这里的状态转移方程需要推算的是从(1,1)到这一点的价值,所以使用减号代表左侧和上侧。而且,还需要加上本身的价值。
f[i][j][k][l]=max(f[i−1][j][k−1][l],f[i−1][j][k][l−1],f[i][j−1][k−1][l],f[i][j−1][k][l−1])+a[i][j]+a[k][l]。还有,如果(i,j)和(k,l)是同一个点,那么就只计算一次,详见代码。

初始化f数组

直接定义为全局变量,默认为0即可。

确认循环顺序

因为每个点的推导依赖于左侧和上侧的点,所以i,j,k,l=1−>n。

代码

#include<cstdio>
#include<stack>
#include<iostream>
using namespace std;
int f[20][20][20][20],a[20][20],n,x,y,z,i,j,k,l;
int main(){
    cin >> n;
    while (cin >> x >> y >> z) a[x][y] = z;
    for (int i = 1;i <= n;++i){
        for (int j = 1;j <= n;++j){
            for (int k = 1;k <= n;++k){
                for (int l = 1;l <= n;++l){
                    f[i][j][k][l] = max(max(
                        f[i - 1][j][k - 1][l],max(
                        f[i - 1][j][k][l - 1],
                        f[i][j - 1][k - 1][l])),
                        f[i][j - 1][k][l - 1]) + a[i][j] + a[k][l];
                    if (i == k && j == l) f[i][j][k][l] -= a[i][j];
                }    
            }    
        }    
    }
    cout << f[n][n][n][n];
	return 0;
}

本文使用 文章同步助手 同步