AcWing 1015 摘花生

题目描述:

Hello Kitty想摘点花生送给她喜欢的米老鼠。

她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

Hello Kitty只能向东或向南走,不能向西或向北走。

问Hello Kitty最多能够摘到多少颗花生。

1.gif

输入格式

第一行是一个整数T,代表一共有多少组数据。

接下来是T组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。

每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。

输出格式

对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。

数据范围

1≤T≤100,
1≤R,C≤100,
0≤M≤1000

输入样例:

2
2 2
1 1
3 4
2 3
2 3 4
1 6 5

输出样例:

8
16

分析:

从本题起开始写算法提高课的题解了,首先是数字三角形模型,数字三角形问题见 AcWing 898 数字三角形

题目要求从左上角出发,每次跨越向下或者向右走一步,最终到达右下角终点所有路径中摘得的最大花生数量。状态表示:f[i][j]表示从(1,1)出发走到(i,j)所有路径中摘得的最大花生数目。由于每次只能向下或者向右走,所以从(1,1)到(i,j)中所有路径中最后一步只可能是从(i-1,j)或者(i,j-1)走向(i,j)的,我们选择其中摘得花生数目较大的那个路径,再加上(i,j)格子上的花生数目就是从(1,1)走到(i,j)所有路径中可获得的最大花生数目了。因此易得状态转移方程为f[i][j] = max(f[i-1][j],f[i][j-1]) + w[i][j],其中w[i][j]表示(i,j)格子上的花生数目。

考虑到状态的无后效性,我们在计算f[i][j]时,需要确保之前的两个状态f[i-1][j]和f[i][j-1]均已经计算完毕了。即计算某个状态前,该状态左边和上边的状态均已计算完成,所以可以用自左而右,自上而下的遍历二维数组的方式来进行状态扩散。为了不特殊处理边界,可以默认f[i][0]和f[0][j]都是哨兵,方格中的边界状态从哨兵节点转移而来相当于之前未获得花生。由于读取花生的权值和状态的扩散顺序是一致的,所以可以在读取一行数据后立刻进行状态的计算。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 105;
int w[N][N],f[N][N];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= m;j++){
                scanf("%d",&w[i][j]);
                f[i][j] = max(f[i-1][j],f[i][j-1])+w[i][j];
            }
        printf("%d\n",f[n][m]);
    }
    return 0;
}

上面代码之所以能够适用于多组测试用例,是因为每次处理不同的方格数据前,尽管f数组已经有了数据,但是所有哨兵的状态依旧是0,从而在状态扩散时可以替换掉旧的状态数据。当然,由于状态转移仅用到左边的和上一行的数据,所以也可以改成滚动数组实现的形式,即将f数组和w数组均改为一维的。计算f[j]时,用到了当前行最新的f[j-1]以及上一行还未替换的数据f[j],所以无须改变状态转移的方向。但是考虑到之前f[1][1]是由f[1][0]和f[0][1]这两个哨兵转移而来,所以多组数据的旧状态不会影响新的f数组,然而采用滚动数组后,f[1]是由f[0]和f[1]转移而来,而f[1]很可能还维持着上一组数据的计算状态,所以采用滚动数组实现计算完一组数据便需要清空f数组。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=105;
int w[N],f[N];
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        int n,m; 
        scanf("%d%d", &n, &m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d", &w[j]),f[j]=max(f[j],f[j-1])+w[j];
        printf("%d\n", f[m]);
        memset(f,0,sizeof f);
    }
    return 0;
}
发布了272 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/104008951
今日推荐