Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
Sample Output
题解:
一开始以为是水搜索,结果和n皇后完全不是一码事啊!!!
额,状态压缩dp吧;
首先,因为是状压DP,方程的某一维一定是一个状态。n<=9,我们可以用01串表示某一行放置的情况。2^0~2^8,那么总共状态就是2^9-1。空间还是承受的起。当然还要枚举一下当前做到第几行,以及当前一共放了几颗棋子。很快,有关f的表示就出来了:用f[i][j][now]表示到第i行,一共放j个棋子(包括这之前的),且第i行的状态是k的方案数。再考虑转移。这一行肯定是由上一行的状态转移过来的,那么我们可以再枚举上一行的状态。
很自然的,发现这会超时。每次枚举一种状态就需要2^9,两重循环已经快爆掉了!我们可以发现一件事情。比如n=5,我们每次枚举到的11111,11011,10111,01011这些状态都是无效的!那么我们可以先预处理一下对于每一行的所有可行的状态(就是不能有连续的1)。这样的效率仍然不高——我们还可以对于每种可行的状态i,j,预处理i和j是否能够相邻,这样我们在DP的时候,就可以O(1)来转移了!
code(无耻地拿了别人的):
- #include<cstdio>
- #define STEP 512
- using namespace std;
- long long f[10][101][STEP],ans;
- int m,n,num,i,j,now,k,stay[101],cnt[101]; //stay记录每种状态压缩后的值,cnt记录对应的状态中1的个数。
- bool map[101][101];
- void dfs(int k,int put,int now) //预处理,k是放了几颗,put是当前放的位置,now是当前状态压缩后的值。
- {
- stay[++m]=now;cnt[m]=k;
- if (k>=(n+1)/2||k>=num) return;
- for (int i=put+2;i<=n;i++)
- dfs(k+1,i,now+(1<<(i-1)));
- }
- void init()
- {
- dfs(0,-1,0); //预处理出每种状态,共有m种。
- for (int i=1;i<=m;i++)
- for (int j=1;j<=m;j++) //<span style="font-family: Arial, Helvetica, sans-serif;">枚举某两种状态是否能相邻。一共有三种不能的情况:上下都是1,左上、右下是1,左下、右上是1。 </span>
- map[i][j]=map[j][i]=((stay[i]&stay[j])||((stay[i]>>1)&stay[j])||((stay[i]<<1)&stay[j]))?0:1; for (int i=1;i<=m;i++)f[1][cnt[i]][i]=1ll; //边界
- }
- int main()
- {
- scanf("%d%d",&n,&num);
- init();
- for (i=2;i<=n;i++)
- {
- for (j=0;j<=num;j++)
- {
- for (now=1;now<=m;now++)
- {
- if (cnt[now]>j) continue;
- for (k=1;k<=m;k++)
- if (map[k][now]&&cnt[k]+cnt[now]<=j) f[i][j][now]+=f[i-1][j-cnt[now]][k]; //转移
- }
- }
- }
- for (i=1;i<=m;i++)
- ans+=f[n][num][i];
- printf("%I64d",ans);
- return 0;
- }
自己的(差不多):
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
long long f[10][101][1024],ans;
int stay[101],cnt[101],num,n,m;
bool _is[1024][1024];
void dfs(int k,int put,int now){
stay[++m]=now;
cnt[m]=k;
if(k>=(n+1)/2||k>num)return;
for(int i=put+2;i<=n;i++)
dfs(k+1,i,now+(1<<(i-1)));
}
int main(){
cin>>n>>num;
dfs(0,-1,0);
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++){
if((stay[i]&stay[j])||((stay[i]<<1)&stay[j])||((stay[i]>>1)&stay[j]))_is[i][j]=false;
else _is[i][j]=true;
}
for (int i=1;i<=m;i++)f[1][cnt[i]][i]=1ll;
for(int i=2;i<=n;i++)
for(int j=0;j<=num;j++)
for(int now=1;now<=m;now++)
{
if(cnt[now]>j)continue;
for(int k=1;k<=m;k++)
if(_is[k][now]&&cnt[now]+cnt[k]<=j)f[i][j][now]+=f[i-1][j-cnt[now]][k];
}
for(int i=1;i<=m;i++)
ans+=f[n][num][i];
cout<<ans;
return 0;
}