poj1321 棋盘问题,状态压缩dp

6 篇文章 0 订阅
好久不做题了。

虽然是水题,还是写个解题报告吧。

普通的暴搜要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;
}









  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值