状压DP

一.状压DP实质

1.压缩状态:当一个状态是多个(最多不超过20,因为1<<20就时1e6了,放不下了)0/1状态的量的集合时,用枚举状态压缩的方式来枚举每一层的不同状态

状态转移:判断两层的两个状态间能否转移就好

2.总结:只是用了不同的方式枚举每一层的状态,本质上还是DP思想

 

二.例题

1.题目1

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cmath>
#include<cstring>
using namespace std;

#define kuan 1<<11

#define LL long long
LL f[12][kuan];
LL h, w, W;
bool fit(LL last, LL now)
{
    bool right = 1;
    for(LL i = 0; i < w; i++)
    {
        LL wei = 1<<i;
        if(!(last & wei))//上一层的wei位无
        {
            if(!(now & wei))
            {
                right = 0;//这一层就该有
                break;
            }
        }
        else if(last & wei)//上一层wei有
        {
            if(!(now & wei))//这一层可以没有
                continue;
            else//但是有的话这一层的下一位也必须要有
            {
                if(i == (w-1))//如果是最后一位就直接不可能了
                {
                    right = 0;
                    break;
                }
                LL nextwei = 1<<(i+1);//下一位
                if((now & nextwei) && (last & nextwei))//上一层wei也必须有,下一层的wei也必须有
                {
                    i++;//下一位就不用判断了
                    continue;
                }
                else
                {
                    right = 0;//不满足就是错的
                    break;
                }
            }
        }
    }
    return right;
}
int main()
{
    while(~scanf("%lld %lld", &h, &w) && (h || w))//输入成功
    {
        memset(f, false, sizeof(f));//由于要求方案总数,那么得先把方案数全设为0
        W = 1<<w;//压缩状态的上限状态+1
        f[0][W-1] = 1;//f[i][j]表示i层为j状态时的方案数,此处是将0层的全都放满了状态赋值为1
        for(int i = 1; i <= h; i++)//i表示当前层数
            for(int j = 0; j < W; j++)//上一层的状态
            {
                if(!f[i-1][j])//如果f==0就没必要判断能否转移了(剪枝)
                    continue;
                for(int k = 0; k < W; k++)//这一层的状态
                    if(fit(j, k))//如果能转移
                        f[i][k] += f[i-1][j];//状态转移
            }
        printf("%lld\n", f[h][W-1]);//输出最终层也被搞满了的样子
    }
    return 0;
}

 

posted @ 2021-04-06 20:21  bear_xin  阅读(43)  评论(0)    收藏  举报