Description
但是现在有一个小小的问题需要解决,托米家的飞机每排有 个座位,有 排座位。因此座椅形成了 的网格(忽略过道),公司为每次航班都出售K张票。
了满足口号中的“翅膀”部分,座位必须遵守以下规则:座位被占用时,座位正前方和座位后方的座位以及当前座位左边和右边必须是空的(大概是因为这个飞机会很大吧, 就是这么任性哼)。
然后为了满足口号中的“独特体验”部分。公司则是对每一趟航班飞机的座位采取不同的安排,如果这一趟的某个座位是占用的,而另一趟的座位是空的,则这两趟飞机座位安排是不同的。
给你三个数字 和 。
现在需要从这些座位中选出 个合法的座位。由于这个数字可能非常大,我们只求它对 取模的结果。
Input
输入的第一行包含一个整数 ,表示指定测试用例的数量。
每个测试用例前面都有一个空白行。
每个测试用例由包含三个整数 和 的一行组成。
Output
对于每个测试用例输出一行,表示答案对 取模的结果。
Sample Input
3
2 3 2
2 4 4
2 5 1
Sample Output
8
2
10
Solution
由于
,故
中的较小值不会超过
,假设
,考虑该
行
列矩阵,把每行状态状压,以
表示前
行考虑完毕后,已经安排了
个合法座位且第
行的座位使用状态为
的方案数,枚举第
行的状态
,需要满足
中不存在相邻两个位置为
,且
和
没有同为
的位置,如此可以使得
和
放在一起后座位也合法,假设
表示
中的
的个数,那么有转移
即为答案,时间复杂度
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;
}