Newcoder 111 C.托米航空公司(狀壓)
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;
}