P2051 中国象棋 - DP - 包含了一些dp时常见的小错误

49 篇文章 0 订阅
6 篇文章 0 订阅

可以三进制状压,写个进制转换函数,可以存在整数型数组里,然后预处理一下,跟二进制没啥差别。。。
但是难写,数据范围也不够

一个化繁为简的思维方式

我们不需要知道棋盘具体的状态,准确来说,对于“同构”的棋盘状态(虽然同构不是这个意思,但是我找不到别的形容词了。。。),可以用一个棋盘状态去代替,只要这个状态所对应的值是所有“同构”的方案之和,就可以等效替代

比如说目前有三种棋盘状态,但是可以忽略他们具体是什么样的,用一个状态去代替他们三个,然后这个状态存的方案数是3就好了

因此设f(i,j,k)代表到第i行,有j列放了0个,k列放了1个,m-j-k放了2个棋子的方案数
(可以看作用一个1 ~ j列放了0个,j+1~k列放了1个,k+1 ~ m列放了两个棋子的棋盘等效代替了许多状态)

然后就按这个1j列放了0个,j+1k列放了1个,k+1~m列放了两个棋子的棋盘,去模拟转移就好了,因为这个棋盘所存的方案数不仅仅是他本身的方案数,也是和他“同构”的棋盘方案数之和。这是一种化繁为简的思路,这样模拟转移就十分好想了

这题有些坑点,一是边界处理,dp时可能会有负下标,二是 C n 2 C_n^2 Cn2的计算,我自作聪明地写了a/2*(a-1),但这样错了,因为只有a(a-1)是一定被2整除的,而其他数先除以2自然会发生向下取整,这个式子要是爆int就开long long吧。。。*
所以再次体现了静态差错的重要。。。得看看哪里容易出问题,然后去试着卡掉自己,这样才能有效地检查低级错误

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100 + 10;
const int MOD = 9999973;
int n,m;
long long ans, f[MAXN][MAXN][MAXN];
int calc(int base, int kk) {
	if(kk == 1) return base;//最好别写k == 1因为容易写成k = 1,所以这行应该删掉,我为了提醒就留下了
	return base * (base-1) / 2;
}

int main() {
	scanf("%d%d", &n, &m);
	f[0][m][0] = 1;
	for(int i=1; i<=n; i++) {
		for(int j=0; j<=m; j++) {
			for(int k=0; k<=m; k++) {
				f[i][j][k] += f[i-1][j][k];
				f[i][j][k] %= MOD;

				if(k >= 1)//为了边界处理多写几个if很重要
					f[i][j][k] += f[i-1][j+1][k-1] * (j+1);
				f[i][j][k] %= MOD;

				if(k >= 2)
					f[i][j][k] += f[i-1][j+2][k-2] * calc(j+2, 2);
				f[i][j][k] %= MOD;

				f[i][j][k] += f[i-1][j][k+1] * (k+1);
				f[i][j][k] %= MOD;

				f[i][j][k] += f[i-1][j][k+2] * calc(k+2, 2);
				f[i][j][k] %= MOD;

				f[i][j][k] += f[i-1][j+1][k] * (j+1) * k;
				f[i][j][k] %= MOD;
			}
		}
	}
	for(int i=0; i<=m; i++) {
		for(int j=0; j<=m; j++) {
			ans += f[n][i][j];
			ans %= MOD;
		}
	}
	printf("%lld", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值