51nod-1371 填数字

基准时间限制:1 秒 空间限制:131072 KB 分值: 160  难度:6级算法题
 收藏
 关注

有n行格子,第i(1<=i<=n)行有i个格子,每行格子是左对齐。现在要在每一个格子填入一个非负整数,最后使得每一行每一列的和都不超过2。

请计算有多少种方案,答案比较大,请输出对100,000,007(1e8+7)取余后的结果。

下图是n=4的时候格子的摆放。


Input
第1行:给出一个整数n (1<=n<=200)。
Output
对于每个测试用例,输出结果占一行。
Input示例
1
Output示例
3

题解:设dp[i][j][k]为到达第i行时有j列和为1,k列和为2的数量。

则第有7种状态能到达第i行。

首先每行和至多为2,首先考虑第i行全是0:

dp[i][j][k] = dp[i - 1][j][k];

其次考虑第i行往上加一个1.可以在列和为0的列加1,也可以在列和为1的列加1:

dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j + 1][k - 1] * (j + 1)) % mod;

dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 1][k] * (i - j - k + 1)) % mod;

考虑在第i行加一个2

dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k - 1] * (i - j - k + 1)) % mod;

考虑在第i行加两个1,可以在两个列和为0的列,可以在两个列和为1的列, 可以在列和为1和列和为0的列;

dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k - 1] * j * (i - j - k + 1)) % mod;

dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 2][k] * (i - j - k + 2) * (i - j - k + 1) / 2) % mod;

dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j + 2][k - 2] * (j + 2) * (j + 1) / 2) % mod;

最后将第n行的所有情况加起来即可(注意取模)

AC代码

#include <stdio.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <algorithm>
#include <string.h>
#include <cmath>
typedef long long ll;
 
using namespace std;

const ll maxn = 205, mod = 1e8 + 7;

ll dp[maxn][maxn][maxn] = {0};

int main(){
	ll n;
	scanf("%lld", &n);
	dp[0][0][0] = 1;
	for(ll i = 1; i <= n; i++)
		for(ll j = 0; j <= i; j++)
			for(ll k = 0; k + j <= i; k++){
				dp[i][j][k] = dp[i - 1][j][k];		//第i行不放 
				if(j - 1 >= 0)
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 1][k] * (i - j - k + 1)) % mod; //第i行放一个1在列和为0的位置 
				if(k - 1 >= 0){
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j + 1][k - 1] * (j + 1)) % mod;  //第i行放一个1在列和为1的位置 
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k - 1] * (i - j - k + 1)) % mod;	//第i行放一个2在列和为0的位置 
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k - 1] * j * (i - j - k + 1)) % mod;//第i行放一个1在列和为1的位置,放一个1在列和为0的位置 
				}
				if(j - 2 >= 0)
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - 2][k] * (i - j - k + 2) * (i - j - k + 1) / 2) % mod; //第i行放两个1在列和为0的位置 
				if(k - 2 >= 0)
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j + 2][k - 2] * (j + 2) * (j + 1) / 2) % mod;//第i行放两个1在列和为1的位置
			}
	ll ans = 0;
	for(ll i = 0; i <= n; i++)
		for(ll j = 0; j <= n; j++)
			ans = (ans + dp[n][i][j]) % mod;
	printf("%lld\n", ans);
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值