P1896 [SCOI2005]互不侵犯
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
注:数据有加强(2018/4/25)
输入输出格式
输入格式:
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式:
所得的方案数
::::::
题目十分简短:观察数据范围我们可以知道:这题有庞大的状态量,所以我们就用状压DP解决问题
dp思路:三维,第一维表示行数,第二维表示状态(二进制),第三维表示已经放了的棋子数(说实话做题做多了会有套路的,有数量限制的dp一般都要开一维表示用了的数量)
WA点:long long型
代码:
/***********************************************/ const int maxn= 1<<10; ll d[12][maxn][120];//当第i行为j方案时,放了z个国王,前i行的方案数 int can[maxn]; int sum_1(ll n)//计算n二进制中1的个数 { int ans=0; while(n) { if( (n&1) >0 ) ans++; n=(n>>1); } return ans; } bool is(int n,int m) { if( (n&m)==0 && (n&(m<<1))==0 && (n&(m>>1))==0 ) return true; return false; } int main() { int n,k; cin>>n>>k; ll mos=(1<<(n))-1; for(ll i=0;i<=mos;i++) if( ( i & (i<<1) ) ==0 && ( i & (i>>1) ) ==0 ) can[i]=1; for(ll j=0;j<=mos;j++) if( can[j] && sum_1(j)<=k ) d[1][j][sum_1(j)]=1; for(int i=2;i<=n;i++) for(ll j=0;j<=mos;j++) //暴力每种方案 if( can[j] ) for(ll k1=0;k1<=mos;k1++) //对前一行 if( can[k1] && is(j,k1) ) //is:与前一行是否冲突 for(ll l=k;l>=sum_1(j);l--) //此循环表示枚举第i行及之前行所有的国王数 d[i][j][l]+=d[i-1][k1][l-sum_1(j)]; ll ans=0; for(ll i=0;i<=mos;i++) ans+=d[n][i][k]; cout<<ans<<endl; return 0; }