原题:
223. Little Kings
time limit per test: 0.5 sec.
memory limit per test: 65536 KB
input: standard
output: standard
After solving nice problems about bishops and rooks, Petya decided that he would like to learn to play chess. He started to learn the rules and found out that the most important piece in the game is the king.
The king can move to any adjacent cell (there are up to eight such cells). Thus, two kings are in the attacking position, if they are located on the adjacent cells.
Of course, the first thing Petya wants to know is the number of ways one can position k kings on a chessboard of size n × n so that no two of them are in the attacking position. Help him!
Input
The input file contains two integers n (1 ≤ n ≤ 10) and k (0 ≤ k ≤ n2).
Output
Print a line containing the total number of ways one can put the given number of kings on a chessboard of the given size so that no two of them are in attacking positions.
Sample test(s)
Input
Test #1
3 2
Test #2
4 4
Output
Test #1
16
Test #2
79
大意:
给你一个尺寸为n×n的棋盘,上面放k个国王。每个国王的攻击范围为周围一圈的8个格。现在问你尺寸n×n的棋盘放k个国王有多少种放置方法。
//#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
using namespace std;
//fstream in,out;
long long dp[11][1025][101];
int one[1025];//1的个数
int state[1025];//状态数
int n,k,index;
int TotalOne(int x)//计算一个二进制数里面有多少个1
{
int coun=0;
while(x)
{
x=x&(x-1);
coun++;
}
return coun;
}
bool LineCmp(int i)//判断一个状态(也就是棋盘上的一行)是否满足要求
{
for(int j=1,k=1;j<=(1<<n);j=(1<<k),k++)
{
if(j&i)
{
if((j<<1)&i||(j>>1)&i)
return false;
}
}
return true;
}
void init()//初始化
{
memset(dp,0,sizeof(dp));
memset(one,0,sizeof(one));
memset(state,0,sizeof(state));
index=0;
dp[0][1][0]=1;
for(int i=0;i<=(1<<n)-1;i++)//找出一行能出现的所有状态,保存在state中
//以及该状态有多少个1保存在one中
{
int tot=TotalOne(i);
if(LineCmp(i)&&tot<=k&&tot<=(n+1)/2)
{
++index;
one[index]=TotalOne(i);//
state[index]=i;
}
}
}
bool TwoLineCmp(int x,int y)//判断上面一行和下面一行是否冲突
{
if(x&y||(y&(x>>1))||(y&(x<<1)))
return false;
return true;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>k)
{
init();
/* for(int i=1;i<=index;++i)
cout<<bitset<10>(state[i])<<" "<<one[i]<<endl;*/
for(int i=1;i<=n;++i)
{
for(int j=1;j<=index;++j)
{
for(int kk=1;kk<=index;++kk)
{
for(int l=0;l<=k;++l)
{
if(l-one[j]>=0&&TwoLineCmp(state[j],state[kk]))
dp[i][j][l]+=dp[i-1][kk][l-one[j]];
}
}
}
}
long long ans=0;
for(int i=1;i<=index;++i)
ans+=dp[n][i][k];
cout<<ans<<endl;
}
return 0;
}
解答:
教程上推荐的那个sgu上的题目,也是我的第一道sgu上面提交的题目。
刚刚接触状态压缩,转移方程还是挺好想的,dp[i][x][k]+=dp[i-1][y][k-one(x)]。
表示第i行在状态x下使用k个国王的摆放方式等于所有第i-1行状态y下使用k减去状态x用的国王数之和。这里的状态和数目都是保存在一个数组中!
我代码能力挺烂的,就没用递归来枚举所有状态。最后把所有计算到n行且国王用了k个的状态加在一起就是答案。