好题。果然好题,经典了。
列一个计划,清明前做好状压DP。之后就刷剩下的MULTI。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int Status=(1<<8); int dp[2][11][Status][Status]; //滚动数组实现。dp[i][j][k][l],其中表示当前状态i(权且认为是i行),推出下一个状态next,i状态以前总共使用了 //j个骑士,ki-1行放骑士的状态,l是i行的状态,这时的放置方法数。答案DP就行。 bool crashpt[Status][Status]; //当前行状态与上两行的状态是否有不相容 bool crashpo[Status][Status]; //当前行(要推出的行)与上一行状是否有冲突 int total[Status]; //每种状态会用了多少个骑士。 int G[10],n; //G[i]表示第i行可放骑士的位置的情况,压 缩。 char str[10]; void predo(){ memset(crashpt,false,sizeof(crashpt)); memset(crashpo,false,sizeof(crashpo)); memset(total,0,sizeof(total)); for(int i=0;i<Status;i++){ for(int j=0;j<8;j++){ if(i&(1<<j)) total[i]++; } for(int j=0;j<Status;j++){ if((j&(i>>2))||(i&(j>>2))) crashpo[i][j]=true; if((j&(i>>1))||(i&(j>>1))) crashpt[i][j]=true; } } } void slove(){ int cur=0,next=1; memset(dp[0],0,sizeof(dp[0])); dp[cur][0][0][0]=1; for(int i=0;i<8;i++){ for(int j=0;j<=n;j++){ for(int t=0;t<Status;t++){ for(int o=0;o<Status;o++){ if(!dp[cur][j][t][o]) continue; for(int now=0;now<Status;now++){ if(j+total[now]>n) continue; if((now&G[i+1])!=now) continue; if(crashpt[now][t]||crashpo[now][o]) continue; dp[next][j+total[now]][o][now]+=dp[cur][j][t][o]; } } } } memset(dp[cur],0,sizeof(dp[cur])); swap(cur,next); } int ans=0; for(int i=0;i<Status;i++){ for(int j=0;j<Status;j++) ans+=dp[cur][n][i][j]; } printf("%d\n",ans); } int main(){ int T; scanf("%d",&T); predo(); while(T--){ scanf("%d",&n); memset(G,0,sizeof(G)); for(int i=1;i<=8;i++){ scanf("%s",str); for(int j=0;j<8;j++){ G[i]<<=1; if(str[j]==‘.‘) G[i]|=1; } } slove(); } return 0; }
时间: 2024-10-25 21:25:56