状压dp(模版及基础例题)(P1896 [SCOI2005]互不侵犯)

状压dp:

简单来说,就是基于集合的dp。本题中状态转移取决于走过的点集和当前位置(最终答案由走完了所有点的状态再回到起点)。

状压就在于记录走过的点集的状态,相当于用二进制表示。

假设0表示该点没走过,1表示走过,对于一个四个点的情况,共有16种状态

简单地说:就是用01表示一个点(位置)的使用情况,并把整个情况压缩成一个二进制数,

通过处理这个二进制数,就能用更少的运算次数和空间复杂度完成状态的转移

(其实状压DP不过就是将一个状态转化成一个数,然后用位运算进行状态的处理)

 

示例:对于一个n^2的二维01矩阵

   0 0 0 0

   0 0 1 0

   1 0 1 0

   1 1 1 1

   压缩之后,变为1111010101000000,存进数组就是把这个二进制数存成十进制数

   注意:由于对于一个数,是从最小位开始,所以把状态写成二进制数也是从最小位开始

   

例题:洛谷(分析都写在注释里)

P1896 [SCOI2005]互不侵犯

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

注:数据有加强(2018/4/25)

输入格式

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式

所得的方案数

输入输出样例

输入 #1
3 2
输出 #1
16
#include<cstdio>
using namespace std;
const int N = 12;
long long dp[N][1<<N][N*N];//前i行,状态为j,放了k个国王 
int n, k, cnt;
long long ans;
int s[1<<N],num[1<<N];//s统计编号为j的状态压缩所产生的二进制数 
int main()//num统计编号为j的状态,所放置的国王数 
{
    scanf("%d%d", &n, &k);
    for(int i = 0;i<(1<<n);i++)//i是当前态压缩所产生的二进制数 
    {
        if(i&(i<<1)) continue;//如果正上方已经放了一个国王,这种情况就显然不可取 
        int sum = 0;
        for(int end = 0;end < n;end++)
        {
            if(i&(1<<end)) sum++;//在二进制数上依次向左找,统计共有几个国王    
        }
        s[++cnt] = i;
        num[cnt] = sum;
    }//预处理 (这一次预处理只要保证i的位置正上方没有国王就可以了)  
    dp[0][1][0] = 1;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= cnt;j++)//枚举状态(状态的编号) 
        {
            for(int p = 0;p <= k;p++)
            {
                if(p>=num[j])//num没有超过k(超过了就显然出错了啊) 
                {
                    for(int end=1;end<=cnt;end++)//枚举第i-1行可行的状态编号 
                    {
                        if(!(s[end] & s[j]) && !(s[end] & (s[j] << 1)) && !(s[end] & (s[j] >> 1)))//从第i-1行向第i行转移 
                        {
                            dp[i][j][p] += dp[i-1][end][p-num[j]];//加和    
                        }
                    }//关于三个判断条件:(1)end和j没有一位同为1(即没有竖直方向上的相同) 
                }//(2),(3)分别判断左上,右下和左下,右上不同时出现1 
            }
        }
    }
    for(int i = 1;i <= cnt;i++)
    {
        ans += dp[n][i][k];//把所有合法的状态数和起来    
    }
    printf("%lld", ans);
    return 0;    
}

 

 

 

 

posted @ 2021-05-13 20:45  Mint-hexagram  阅读(70)  评论(0编辑  收藏  举报