题链
dp维数不够?再加一维。还不够?再加再加!
当使用dp数组的时候,发现一个元素表示的情况仍然存在许多情况,需要再细分才能写出状态转移方程,那就再加一维试试。
做难题第一次这样一发入魂(ac),开心!
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
注:数据有加强(2018/4/25)
输入格式
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式
所得的方案数
输入输出样例
输入 #1
3 2
输出 #1
16
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=(1<<9)+10;
ll c[100];
ll dp[10][N][100];
ll countt[N];
void init_countt(int n){
int sm=(1<<n)-1;
for(int s=1;s<=sm;++s)
for(int i=0;i<=n-1;++i){
int i1=i+1;
if(
(i<n-1)
&&
(s& (1<<i) )
&&
(s& (1<<i1) )
){
countt[s]=-1;
break;
}
if(s&(1<<i))++countt[s];
}
}
int main(){
int n,k;
cin>>n>>k;
int sm=(1<<n)-1;
init_countt(n);
ll ans=0;
for(int s=1;s<=sm;++s){
if(countt[s]<0)continue;
++dp[0][s][countt[s]];
ans+=dp[0][s][k];
}
for(int i=1;i<=n-1;++i){
for(int s=0;s<=sm;++s){
if(countt[s]<0)continue;
for(int s0=0;s0<=sm;++s0){
if(countt[s0]<0)continue;
if(s&s0) continue;
if((s>>1)&s0) continue;
if((s<<1)&s0) continue;
for(int j=0;j<=k-countt[s];++j)
dp[i][s][j+countt[s]]+=dp[i-1][s0][j];
}
ans+=dp[i][s][k];
}
}
cout<<ans<<endl;
return 0;
}
教训:
1.不能使用两次选取,会有重复计算
把满足的情况都选出来,再用组合数Cnm来选取K个位置,会有重复计算。假如位置1,2,3,5满足情况,位置1,2,3,6满足情况,要选3个。选取1,2,3的情况就重复计算了。
2.对于代码中
ans+=dp[i][s][k];
不能再加上比k多的情况。如果取了,再从中选取k个,那不论选的是哪k个,该情况都会再别的地方被运算一遍