洛谷 P1373 小a和uim之大逃离

传送门

思路

吐槽

吐槽:话说不应该是他俩都得死嘛qwq

咋做

一道 D P DP DP好题.

我们用 f [ i ] [ j ] [ q ] [ p ] f[i][j][q][p] f[i][j][q][p]来表示:走到第 i i i行第 j j j列魔液差距值为 q q q且当前为 p p p走的方案数( p ∈ { 0 , 1 } p = 0 p\in \{0, 1\} p=0 p{0,1}p=0表示目前为小 a a a走, p = 1 p=1 p=1表示目前为 u i m uim uim走)

初始条件: f [ i ] [ j ] [ a [ i ] [ j ] ] [ 0 ] = 1 f[i][j][a[i][j]][0]=1 f[i][j][a[i][j]][0]=1表示小 a a a从每个点开始取,差距值为 a [ i ] [ j ] a[i][j] a[i][j]的方案数为 1 1 1

那么我们可以想出转移方程:

f [ i ] [ j ] [ p ] [ 0 ] + = f [ i − 1 ] [ j ] [ p − a [ i ] [ j ] ] [ 1 ] + f [ i ] [ j − 1 ] [ p − a [ i ] [ j ] ] [ 1 ] f[i][j][p][0]+=f[i-1][j][p-a[i][j]][1]+f[i][j-1][p-a[i][j]][1] f[i][j][p][0]+=f[i1][j][pa[i][j]][1]+f[i][j1][pa[i][j]][1]

这个式子表示目前在第 i i i行第 j j j列,差距值为 p p p,当前小 a a a走的方案数,因为只能往右走或往下走,且上一步一定是 u i m uim uim走的,所以可以从第 i − 1 i-1 i1行第 j j j列、第 i i i行第 j − 1 j-1 j1列转移过来,差距值增大

f [ i ] [ j ] [ p ] [ 1 ] + = f [ i − 1 ] [ j ] [ p + a [ i ] [ j ] ] [ 0 ] + f [ i ] [ j − 1 ] [ p − a [ i ] [ j ] ] [ 0 ] f[i][j][p][1]+=f[i-1][j][p+a[i][j]][0]+f[i][j-1][p-a[i][j]][0] f[i][j][p][1]+=f[i1][j][p+a[i][j]][0]+f[i][j1][pa[i][j]][0]

同理,这个式子表示目前在第 i i i行第 j j j列,差距值为 p p p,当前 u i m uim uim走的方案数,上一步一定是小 a a a走的,所以可以从第 i − 1 i-1 i1行第 j j j列、第 i i i行第 j − 1 j-1 j1列转移过来,差距值减小

我们容易想出,最后的答案就是 ∑ i = 1 n ∑ j = 1 m f [ i ] [ j ] [ 0 ] [ 1 ] \sum_{i = 1}^{n}\sum_{j = 1}^{m}f[i][j][0][1] i=1nj=1mf[i][j][0][1]最后一维是 1 1 1是因为最后一步只能由 u i m uim uim

空间复杂度 n ∗ m ∗ k ∗ 2 n*m*k*2 nmk2刚好可以,时间复杂度 O ( n ∗ m ∗ k ) O(n*m*k) O(nmk)可以过

注意!

  1. 因为魔液值到达 k + 1 k+1 k+1之后就会清 0 0 0,所以在过程中要随时对 k + 1 k+1 k+1取模
  2. 空间开好!!不要开太大!因为最后一维只用 0 、 1 0、1 01,所以开 2 2 2的数组就好了就行了,开 3 3 3 M L E MLE MLE
  3. 码风丑陋,谨慎阅读 q w q qwq qwq

代码

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

const int B = 20;
const int A = 800 + 7;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, m, k, a[A][A], f[A][A][B][2];

int main() {
	n = read(), m = read(), k = read() + 1;
	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) a[i][j] = read(), f[i][j][a[i][j] % k][0] = 1;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			for(int p = 0; p <= k; p++) {
				f[i][j][p][0] = (f[i][j][p][0] + f[i - 1][j][(p - a[i][j] + k) % k][1] + f[i][j - 1][(p - a[i][j] + k) % k][1]) % mod;
				f[i][j][p][1] = (f[i][j][p][1] + f[i - 1][j][(p + a[i][j] + k) % k][0] + f[i][j - 1][(p + a[i][j] + k) % k][0]) % mod;
			}
	int ans = 0;
	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) ans += f[i][j][0][1], ans %= mod;
	cout << ans << '\n';
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值