POJ 1321 棋盘问题

#include<stdio.h>
#include<string.h>
int check(int a,int m)
{
    int b=0;
    while(a>0)
    {
        if(a&1)
            b++;
        a>>=1;
    }
    if(b==m)
        return 1;
    return 0;
}
int main()
{
    int n,m,dp[(1<<8)+5][10],Map1[10],s[1000],len;//dp[i][j]定义状态为在j行的状态为i
    char Map[10][10];
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0||m==0)
        {
            printf("0\n");
            continue;
        }
        if(n==-1&&m==-1)
            break;
        for(int i=0; i<n; i++)
            scanf("%s",Map[i]);
        memset(Map1,0,sizeof(Map1));
        for(int i=0; i<n; i++)//压缩地图
        {
            for(int j=0; j<n; j++)
                if(Map[i][j]=='.')
                    Map1[i]=Map1[i]|(1<<(n-1-j));
        }
        len=0;
        for(int i=0; i<(1<<n); i++)//把所有的有m个1的状态存到一个数组里
            if(check(i,m))
                s[len++]=i;
        memset(dp,0,sizeof(dp));
        for(int j=0;j<n;j++)//把所有为1个1的都都初始化为1,由这些点这些为只有一个1的状态去扩展,所以这个能用dp的本质是算出来所有一个情况,然后扩展成两个的情况,慢慢扩展,所以最后求一下题目要求的m的所有状态的和就行了
        {
            for(int i=1; i<(1<<n); i=(i<<1))
                if((Map1[j]&i)==0)
                    dp[i][j]=1;
        }
        for(int i=1; i<n; i++)//枚举行
        {
            for(int j=0; j<(1<<n); j=j==0?1:j<<1)//直接枚举当前行的状态是0,1,2,4,8.(也就是只有一个1)
            {
                if((j&Map1[i])==0)//判断是否和地图矛盾
                    for(int k=0; k<(1<<n); k++)//枚举上一行的状态
                        if((k&j)==0)//判断两个状态是否矛盾,当j==0的时候表示当前行不放,当j不等于0的时候是放在那,所以当j放一个位置的时候无论扩展到那个一个状态,都等于1*dp[k][i-1]中情况所以直接加上就行
                            dp[k|j][i]+=dp[k][i-1];
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<(1<<n);j++)
                printf("%d ",dp[j][i]);
            puts("");
        }
        int ans=0;
        for(int i=0; i<len; i++)
                ans+=dp[s[i]][n-1];
        printf("%d\n",ans);
    }
    return 0;
}
/*
4 3
#..#
#.#.
..#.
#.##
5 4
#..#.
#.#..
..#..
#.##.
.####
3 3
###
###
###
*/

/*
5 4
#.#.
##..
.#..
###.
.###
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值