二维dp+背包

前面的碎碎念

不得不说,这次的小白月赛的题目真的是很好,有的题目让人耳目一新,有的题目让人思维拓展,更有的题目昨晚直呼太爽了,而这个题目真的很让人耳目一新,没想到还可以这么做。

题目描述
有一个n×m的网格地图,每个点有个值

,现在牛牛要从(1,1)走到(n,m),他可以往右边或者往下走,每次到一个点会获得当前的点权值,并将权值和mod 1e4+7,当牛牛从不同方式走到(n,m)的时候能获得多少种权值和?

题目分析:题目要求的是一共有多少个数字可以由a数组构成,那么我们便可以枚举mod范围内所有的数字的情况,看看a数组的元素能不能组成这个数,因为题目是二维dp问题,每次可以往下和往右移动一次,那么我们便增加一维用于记录mod范围内的数字,然后依次枚举,我们维护一个状态,每次从上和左边移动下来,但是这个题目还要注意一个地方,那就是相减可能会出现负数,(因为我们前面模了一下)所以我们要在计算的时候,加一个mod并且对减去后的数字模一下,这样就能防止溢出(防止出现负数),最后我们在枚举每个数字,如果这个数字能被a数组组成,那么三维dp里面的数就不为0.

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e4 + 7;
int a[1010][1010];
bool dp[110][110][10010];
int main()
{
    int n, m;
    int ans = 0;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            scanf("%d", &a[i][j]);
            a[i][j] %= mod;
        }
    dp[1][1][a[1][1]] = 1;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            for (int k = mod - 1; k >= 0; --k)
            {
                dp[i][j][k] += dp[i - 1][j][(k - a[i][j] + mod) % mod];
                dp[i][j][k] += dp[i][j - 1][(k - a[i][j] + mod) % mod];
            }
    for (int i = 0; i < mod; ++i)
        if (dp[n][m][i])
            ++ans;
    printf("%d\n", ans);
}