题目大意:给出一张n*n的棋盘和k个王,问有多少种使得王互不相攻击的方案。
题解:状压dp,设dp[i][j][S]表示前i行用了j个王,其中第i行放王的状态是S。
显然有dp[i][j][S]+=dp[i][j-cnt[S]][S'],其中cnt数组保存每个状态王的个数,且S'作为当前行可以在下一行放S这个状态。
位运算写错了妈了个鸡,还有用vector里的元素时直接用的指针,就这两个地方,第一次交WAontest 30,第二次A了。
%%%__debug大神,感谢Dash。
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int MAXN=11,MAXK=101,MAXS=1025;
int n,k;
long long dp[MAXN][MAXK][MAXS];
int cnt[MAXS];
vector<int> s[MAXS];
bool vis[MAXS][MAXS];
void get1(int x)
{
if(x==16)
x=16;
int tmp=x;
while(tmp)
{
if(tmp&1)cnt[x]++;
tmp>>=1;
}
}
void dfs(int from,int x,int now)
{
if(!vis[from][now])s[from].push_back(now),vis[from][now]=1;
if(!(x^((1<<n)-1)))return;
int tmp=x,cnt=0,check=x;
while(cnt<n)
{
if(!(tmp&1))
{
if(cnt>0)check|=(1<<cnt-1);
if(cnt<n-1)check|=(1<<cnt+1);
check|=(1<<cnt);
now|=(1<<cnt);
dfs(from,check,now);
check=x;
now&=((1<<(10+1))-1)^(1<<cnt);
}
cnt++;
tmp>>=1;
}
}
void getSuf(int x)
{
int tmp=x,cnt=0,check=0;
while(cnt<n)
{
if(x&1)
{
if(cnt>0)check|=(1<<cnt-1);
if(cnt<n-1)check|=(1<<cnt+1);
check|=(1<<cnt);
}
cnt++;
x>>=1;
}
dfs(tmp,check,0);
}
void debug(int i)
{
cout<<i<<' ';
for(int j=0;j<s[i].size();j++)
cout<<s[i][j]<<' ';
cout<<endl;
}
int main()
{
freopen("223.in","r",stdin);
freopen("223.out","w",stdout);
scanf("%d %d",&n,&k);
long long ans=0;
for(int i=0;i<(1<<n);i++)get1(i);
for(int i=0;i<(1<<n);i++)
{
if(i&(i<<1))continue;
getSuf(i);
//debug(i);
}
dp[0][0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++)
for(int l=0;l<(1<<n);l++)
for(int o=0;o<s[l].size();o++)
if(j-cnt[s[l][o]]>=0)
dp[i][j][s[l][o]]+=dp[i-1][j-cnt[s[l][o]]][l];//s[l][o] instead of o,o is just a pointer!
for(int i=0;i<(1<<n);i++)ans+=dp[n][k][i];
cout<<ans<<endl;
}