小a和uim之大逃离

传送门

很不错的一道DP题……

这个题的关键之处在于在两者之间收集的魔液的差值上进行DP(很像多米诺骨牌那道题),我们用dp[i][j][p][0/1]表示在点(i,j),当前两者魔液差值%(k+1)为p,0/1表示小a/uim正在取魔液的方案数,就有DP方程如下:

dp[i][j][h][0] += dp[i][j-1][(h - a[i][j] + k) % k][1],dp[i][j][h][0] %= mod;
dp[i][j][h][0] += dp[i-1][j][(h - a[i][j] + k) % k][1],dp[i][j][h][0] %= mod;
dp[i][j][h][1] += dp[i][j-1][(h + a[i][j]) % k][0],dp[i][j][h][1] %= mod;
dp[i][j][h][1] += dp[i-1][j][(h + a[i][j]) % k][0],dp[i][j][h][1] %= mod; 

这个DP的初始化是对于每一个a[i][j]不为0的点初始化为1,之后直接三重循环DP即可,最后答案枚举所有点累加。

看一下代码。

// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<ctime>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')

using namespace std;
typedef long long ll;
const int M = 1005;
const int mod = 1e9 + 7;

int read()
{
   int ans = 0,op = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9')
   {
      if(ch == '-') op = -1;
      ch = getchar();
   }
   while(ch >= '0' && ch <= '9')
   {
      ans *= 10;
      ans += ch - '0';
      ch = getchar();
   }
   return ans * op;
}

int n,m,k,dp[805][805][20][2],a[805][805];
ll ans;

int main()
{
   n = read(),m = read(),k = read() + 1;
   rep(i,1,n)
      rep(j,1,m) a[i][j] = read(),dp[i][j][a[i][j]%k][0] = 1;
   rep(i,1,n)
      rep(j,1,m)
      rep(h,0,k)
   {
      dp[i][j][h][0] += dp[i][j-1][(h - a[i][j] + k) % k][1],dp[i][j][h][0] %= mod;
      dp[i][j][h][0] += dp[i-1][j][(h - a[i][j] + k) % k][1],dp[i][j][h][0] %= mod;
      dp[i][j][h][1] += dp[i][j-1][(h + a[i][j]) % k][0],dp[i][j][h][1] %= mod;
      dp[i][j][h][1] += dp[i-1][j][(h + a[i][j]) % k][0],dp[i][j][h][1] %= mod;      
   }
   rep(i,1,n)
      rep(j,1,m) ans += dp[i][j][0][1],ans %= mod;
   printf("%lld
",ans);
   return 0;
}
原文地址:https://www.cnblogs.com/captain1/p/9880378.html