好久不做题了。
虽然是水题,还是写个解题报告吧。
普通的暴搜要8^8的复杂度,还是有点危险的。
随便想一下可以发现这题存在最优子结构等动态规划的要素,所以可以用状态压缩DP解决。
状态就是每层,当前已有哪几个列已被占。二维。
转移就是两种:
1.当前行不加棋子,就是f[i][j]+=f[i-1][j];
2.当前行加一个棋子,就是f[i][newst]+=f[i-1][j],而且加棋子的位置不能是空白的。
最后将最后一行棋子数等于k的状态的方案数求和即可。
复杂度O(2^8*8*8),比搜索好了许多。Poj的水数据0ms。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define NN 300
int num[NN],f[12][NN];//f[i][j]表示在做到第i行,满足行状态为j时的可行方案总数
int n;
char g[12][12];
void cal(int now){ //计算某个状态有几个位置被占,也就是已放有几个棋子,存在num[]中
int i,j,tmp,cnt;
tmp=1;
cnt=0;
for(i=1;i<=n;++i){
if (now&tmp) cnt+=1;
tmp=tmp<<1;
}
num[now]=cnt;
}
int main(){
int i,j,l,k,status,newst,ans;
while(1){
scanf("%d%d",&n,&k);
if (n==-1&&k==-1) break;
for(i=1;i<=n;++i){
scanf("%s",g[i]+1);
}
status=1<<n;
for(i=0;i<status;++i) cal(i);
memset(f,0,sizeof(f));
f[0][0]=1;//初始化,空状态有一种可行方案。
for(i=1;i<=n;++i){//以行为阶段转移 O(n)
for(j=0;j<status;++j)if (num[j]<=k){//每一行遍历所有可能的状态 O(2^n)
f[i][j]+=f[i-1][j]; //先加上上一行就是这种状态的方案数
//对于当前状态j,我们需要考虑所有可能转移到的状态newst, O(n)
//状态为当前状态再加上一个棋子的状态
//当然这个加的位置不能是'#',也不能是已该列已被占
//也就是if中的两个条件
for(l=1;l<=n;++l)if ((g[i][l]=='#')&&(j&(1<<(l-1)))==0){
newst=j|(1<<(l-1)); //然后构造出这个新状态newst
f[i][newst]+=f[i-1][j]; //在这一行的此状态中加上上一行的j状态方案数
}
}
}
ans=0;
for(i=0;i<status;++i){
if (num[i]==k) {ans+=f[n][i];}//最后,只要统计最后一行棋子数为k的方案数
}
printf("%d\n",ans);
}
return 0;
}