金兵馅饼

金币馅饼

https://ac.nowcoder.com/acm/contest/6173/E

题目描述

最近,奶牛们热衷于把金币包在面粉里,然后把它们烤成馅饼。第i块馅饼中含有Ni(1<=Ni<=25)块金币,并且,这个数字被醒目地标记在馅饼表面。
奶牛们把所有烤好的馅饼在草地上排成了一个R行(1<=R<=100)C列(1<=C<=100)的矩阵。你现在站在坐标为(1,1)的馅饼边上,当然,你可以拿到那块馅饼里的所有金币。你必须从现在的位置,走到草地的另一边,在坐标为(R,C)的馅饼旁边停止走动。每做一次移动,你必须走到下一列的某块馅饼旁边,并且,行数的变动不能超过1(也就是说,如果现在你站在坐标为(r,c)的馅饼边上,下一步你可以走到坐标为(r-1,c+1),(r,c+1),或者(r+1,c+1)的馅饼旁边)。当你从一块馅饼边经过,你就可以拿走馅饼里所有的金币。当然啦,你一定不会愿意因半路离开草地而失去唾手可得的金币,但,最终你一定得停在坐标为(R,C)的馅饼旁边。
现在,你拿到了一张标记着馅饼矩阵中,每一块馅饼含金币数量的表格。那么,按照规则,你最多可以拿到多少金币呢?

思路

做题时看着数据范围感觉爆搜顺便减下枝(觉得能过的(●ˇ∀ˇ●)...)应该能过,最后只过了70%。。。,然后看了一下大佬的们的题解,都是Dp,所以就参考了一下自己也尝试了一下终于过了。。。

其实Dp的话就很好想了,先确定状态f[i][j]表示从(1,1)走到(i,j)能拾取到的金币数量最大值。

1.那么根据题意不难看出,想要走到(i,j)这个点,只有三种方式,分别是从(i-1,j-1)、(i,j-1)、(i+1,j-1)走过来。

2.通过草稿一下很容易就可以看出,我们是不能走到Σ(i,i)这条线一下的点的,并且当r-i>c-j时最终是走不到(r,c)的,所以如果有想法的大佬(觉得能爆搜+剪枝过的)求教>︿<

3.之后就是推一下状态转移方程了,都说到这了肯定能推出来了吧。。>︿<
f[i][j] = max(f[i-1][j-1],f[i][j-1],f[i+1][j-1]) + a[i][j] (需要注意的是只有当前点能走到才去加上这个点的金币值);

4.对了需要注意的是我们想求f[i][j]这个状态是需要知道f[i-1][j-1],f[i][j-1],f[i+1][j-1]的,所以在for循环枚举的时候要从左下角的点开始枚举并且是从下到上求解的,具体为什么大家草稿一下就清晰了。

Code

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

const int N = 110;
int a[N][N], f[N][N], n, m;

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
            scanf("%d", &a[i][j]);

    for(int i = 1; i <= m; i ++ )
        for(int j = n; j >= 1; j -- )
        {
            if(n - j > m - i || j > i) continue;   //先判断一下当前点是否能走并且 是否能走到(r,c)  (1)
            f[j][i] = max(f[j - 1][i - 1], f[j][i - 1]);
            f[j][i] = max(f[j + 1][i - 1], f[j][i]);
            f[j][i] += a[j][i];
        }

    printf("%d\n", f[n][m]);

    return 0;
}
全部评论

相关推荐

评论
1
收藏
分享
牛客网
牛客企业服务