Newcoder 111 C.托米航空公司(状压DP)

Description

但是现在有一个小小的问题需要解决,托米家的飞机每排有 m m m个座位,有 n n n排座位。因此座椅形成了 m × n m\times n m×n的网格(忽略过道),公司为每次航班都出售K张票。

了满足口号中的“翅膀”部分,座位必须遵守以下规则:座位被占用时,座位正前方和座位后方的座位以及当前座位左边和右边必须是空的(大概是因为这个飞机会很大吧, b o s s boss boss就是这么任性哼)。

然后为了满足口号中的“独特体验”部分。公司则是对每一趟航班飞机的座位采取不同的安排,如果这一趟的某个座位是占用的,而另一趟的座位是空的,则这两趟飞机座位安排是不同的。

给你三个数字 m , n m,n m,n k k k

现在需要从这些座位中选出 k k k个合法的座位。由于这个数字可能非常大,我们只求它对 420047 420047 420047取模的结果。

Input

输入的第一行包含一个整数 T T T,表示指定测试用例的数量。

每个测试用例前面都有一个空白行。

每个测试用例由包含三个整数 m , n m,n m,n k k k的一行组成。

( T ≤ 10 , n ⋅ m ≤ 80 , k ≤ 4 ) (T\le 10,n\cdot m\le 80,k\le 4) (T10,nm80,k4)

Output

对于每个测试用例输出一行,表示答案对 420047 420047 420047取模的结果。

Sample Input

3

2 3 2

2 4 4

2 5 1

Sample Output

8
2
10

Solution

由于 n × m ≤ 80 n\times m\le 80 n×m80,故 n , m n,m n,m中的较小值不会超过 8 8 8,假设 m ≤ 8 m\le 8 m8,考虑该 n n n m m m列矩阵,把每行状态状压,以 d p [ i ] [ S ] [ j ] dp[i][S][j] dp[i][S][j]表示前 i i i行考虑完毕后,已经安排了 j j j个合法座位且第 i i i行的座位使用状态为 S S S的方案数,枚举第 i + 1 i+1 i+1行的状态 T T T,需要满足 T T T中不存在相邻两个位置为 1 1 1,且 S S S T T T没有同为 1 1 1的位置,如此可以使得 S S S T T T放在一起后座位也合法,假设 n u m ( T ) num(T) num(T)表示 T T T中的 1 1 1的个数,那么有转移
d p [ i + 1 ] [ T ] [ j + n u m ( T ) ] + = d p [ i ] [ S ] [ j ] dp[i+1][T][j+num(T)]+=dp[i][S][j] dp[i+1][T][j+num(T)]+=dp[i][S][j]
∑ S d p [ n ] [ S ] [ k ] \sum\limits_{S}dp[n][S][k] Sdp[n][S][k]即为答案,时间复杂度 O ( n m ⋅ 2 2 m ) O(nm\cdot 2^{2m}) O(nm22m)

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define mod 420047
int T,n,m,k,num[(1<<8)+5],dp[81][(1<<8)+5][5];
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x;
}
bool check(int S)
{
	if(S&(S<<1))return 0;
	return 1;
}
int main()
{
	num[1]=1;
	for(int i=2;i<(1<<8);i++)num[i]=num[i/2]+(i&1);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&n,&m,&k);
		if(n<m)swap(n,m);
		int M=1<<m;
		memset(dp,0,sizeof(dp));
		dp[0][0][0]=1;
		for(int i=1;i<=n;i++)
			for(int S=0;S<M;S++)
				if(check(S))
					for(int j=0;j<=4;j++)
						if(dp[i-1][S][j])
							for(int T=0;T<M;T++)
								if(((S&T)==0)&&num[T]+j<=k&&check(T))
									dp[i][T][num[T]+j]=add(dp[i][T][num[T]+j],dp[i-1][S][j]);
		int ans=0;
		for(int S=0;S<=M;S++)ans=add(ans,dp[n][S][k]);
		printf("%d\n",ans);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值