题意:给定n,k,在n*n个棋盘里放k个棋子,让他们不能互相攻击,有多少种方案,一个棋子可以攻击他的八个方向。
题解:很久没打状压DP了,一脸懵逼,本来状压就不是很好,现在弱的一匹。。表示这题一脸不可做,不得已去膜了一波题解。
有一个很好的题解http://blog.csdn.net/qpswwww/article/details/34516641
这题主要是如果直接dp状态数会很大,那么我们就可以把每一行的状态压缩一下,然后提前预处理每一行的方案数,然后再预处理前两行的合法性(不被攻击),接着就能直接往下dp了,细节有点多,看不懂的话看代码,代码看不懂上面那个网址的代码的注释写的很好,比较推荐。
#include<stdio.h>
#define N 100
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll f[N][N][600],ans;
int stay[N],map[N][N],cnt[N],n,k,m=0;
inline void predfs(int x,int pos,int now)
{
int i;
stay[++m]=now;
cnt[m]=x;
if (x>=(n+1/2)||x>=k)return;
fo(i,pos+2,n)
predfs(x+1,i,now+(1<<(i-1)));
}
inline void premap()
{
int i,j;
fo(i,1,m)
fo(j,1,m)
{
map[i][j]=map[j][i]
=((stay[i]&stay[j])||((stay[i]>>1)&stay[j])||((stay[i]<<1)&stay[j]))?0:1;
}
fo(i,1,m)
f[1][cnt[i]][i]=1;
}
int main()
{
int i,j,now,h;
scanf("%d%d",&n,&k);
predfs(0,-1,0);
premap();
fo(i,2,n)
{
fo(j,0,k)
{
fo(now,1,m)
{
if (cnt[now]>j)continue;
fo(h,1,m)
if (map[h][now]&&cnt[h]+cnt[now]<=j)
f[i][j][now]+=f[i-1][j-cnt[now]][h];
}
}
}
fo(i,1,m)
ans+=f[n][k][i];
printf("%lld\n",ans);
return 0;
}