洛谷p1373 DP

传送门:洛谷p1373
这道题我原本的想法就是很简单的f[i][j][x][y][0/1];就是以第i,j格为终点的方案数目,可是即wa又mle ,最后看了看题解才比较了解。就是我们不去储存两个人瓶子各自有多少,而是去去一个差值,然后差值取模后相等的都是等价的,这里的取模是对k+1取模,因为瓶子差值就是[0,k],所以对k+1取模。
用f[i][j][p][0/1]表示以ij结束的格子差值为p的由0或者1走到这一格子的方案数目。(0表示小a走的,1表示小b走的)
然后我们去对每个ij枚举他们瓶子的差值p
也就是
f[i][j][p][0]+=f[i-1][j][(p-value[i][j]+k)%k][1] (i-1>=1)

f[i][j][p][0]+=f[i][j-1][(p-value[i][j]+k)%k][1] (j-1>=1)

f[i][j][p][1]+=f[i-1][j][(p+value[i][j])%k][0] (i-1>=1)

f[i][j][p][1]+=f[i][j-1][(p+value[i][j])%k][0] (j-1>=1)
怎么理解这个式子。主要是在后面转移那里比较难理解。
我们看这个f[i][j][p][0];他表示在ij结束的格子,差值为p,由小A走到的这一格子的方案数目。
我们设在ij这个格子小a瓶子有ka2,在小u瓶子有ku2;
所以ka2 - ku2 = p;
我们设在i-1,j(i,j-1同理),j这个格子小a瓶子有ka1,在小u瓶子有ku1;
由于ij是由小a走的,所以
ka1 = ka2 - value[i][j];
ku1 = ku2;
联立一下也就是
ka1- ku1 = ka2 - value[i][j] - ku2 = p - value[i][j];

再看f[i][j][p][1] 他表示在ij结束的格子,差值为p,由小U走到的这一格子的方案数目。
我们设在ij这个格子小a瓶子有ka2,在小u瓶子有ku2;
所以ka2 - ku2 = p;
我们设在i-1,j(i,j-1同理),j这个格子小a瓶子有ka1,在小u瓶子有ku1;
由于ij是由小u走的,所以
ka1 = ka2;
ku1 = ku2 - value[i][j];
联立可得
ka1 - ku1 = ka2 - (ku2 - value[i][j]) = p - value[i][j];
但是由于 p - value[i][j]可能减出个负数,所以我们+k在模k就好了;
我相信这样子这个方程已经能很清晰的理解了。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
#include<vector>
using namespace std;
#define ll long long
const int max_ = 800 + 7;
const int p = 1e9 + 7;
inline int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
int f[max_][max_][17][2], value[max_][max_], n, m, k;
int main() {
	n = read(), m = read(), k = read(); k++;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			value[i][j] = read();
			f[i][j][value[i][j]%k][0] = 1;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			for (int z = 0; z <= k-1; z++) {
				if (i - 1 >= 1) {
					f[i][j][z][0] += f[i - 1][j][(z - value[i][j] + k) % k][1];
					f[i][j][z][0] %= p;
					f[i][j][z][1] += f[i - 1][j][(z + value[i][j]) % k][0];
					f[i][j][z][1] %= p;
				}
			if (j - 1 >= 1) {
				f[i][j][z][0] += f[i][j-1][(z - value[i][j] + k) % k][1];
				f[i][j][z][0] %= p;
				f[i][j][z][1] += f[i][j-1][(z + value[i][j]) % k][0];
				f[i][j][z][1] %= p;
			}
			}		
		}
	}
	int t = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			t = (t + f[i][j][0][1]) % p;
		}
	}
	cout << t;
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值