动态规划---状压dp

状压dp,就是把动态规划之中的一个个状态用二进制表示,主要运用位运算。

这里有一道例题:蓝书P639猛兽军团1

直接上代码,注释很详细

#include<cstdio>
#include<iostream>
#include<cstring>
#define N 15
#define M 110
#define MAX 550
using namespace std;
/*
见蓝书641页
*/
int s[MAX]; // 记录一行可能的状态
int num[MAX]; //s数组对应每个状态放了多少个猛兽
int states;
long long f[N][M][MAX]; //f[i][j][k]第i行状态为k,放了j个猛兽
int n,m;
void init_state() //预处理s,num数组,代表一行之内所有的可能性
{
    states = 0;
    for(int i = 0; i < (1 << n); i++) //注意,这里是枚举状态
    {
        if(i & (i << 1)) //处理一排上的冲突情况
            continue;
        int t = i;
        num[states] = 0;
        while(t)
        {
            num[states] += (t & 1);
            t = t >> 1;
        }
        s[states++] = i;  //保存状态
    }
}
void dp()
{
    int a,c,mm,b,cc;
    long long ans;
    memset(f,0,sizeof(f));
    //单独算第一行和最后一行
    for(int i = 0; i < states; i++)
    {
        int j = num[i];
        if(j <= m)  //不能超过总数
            f[1][j][i]++;
    }
    for(int i = 2; i < n; i++) //2~n - 1行
    {
        for(int j = 0; j <= m; j++) // 到第i行,一共放了j个猛兽
        {
            for(a = 0; a < states; a++) //i行状态
            {
                c = num[a];
                if(c > j)
                    continue;
                mm = j - c;//前i - 1行的总数
                for(int b = 0; b < states; b++) //枚举i-1行
                {
                    cc = num[b];
                    if(cc > mm)
                        continue;
                    if(s[a] & s[b]) //上下有攻击
                        continue;
                    if(s[a] & (s[b] << 1)) //对角有攻击
                        continue;
                    if(s[b] & s[a] << 1) // 同上
                        continue;
                    f[i][j][a] += f[i - 1][mm][b];
                }
            }
        }
    }
    ans = 0;
    for(a = 0; a < states; a++) //最后一行
    {
        c = num[a];
        if(c > m)
            continue;
        int j = m - c;
        for(int b = 0; b < states; b++) //枚举n-1行
        {
            cc = num[b];
            if(cc > j)
                continue;
            if(s[a] & s[b]) //上下有攻击
                continue;
            if(s[a] & (s[b] << 1)) //对角有攻击
                continue;
            if(s[b] & s[a] << 1) // 同上
                continue;
            f[n][m][a] += f[n - 1][j][b];
        }
        ans += f[n][m][a];
    }
    printf("%lld\n",ans);
}
int main()
{
    scanf("%d%d",&n,&m);
    init_state();
    dp();
    return 0;
}

原文地址:https://www.cnblogs.com/DukeLv/p/9221913.html

时间: 2024-11-07 19:36:59

动态规划---状压dp的相关文章

玉米田(状压DP)

题目:P1879 [USACO06NOV]玉米田Corn Fields 参考:状态压缩动态规划 状压DP 农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用. 遗憾的是,有些土地相当贫瘠,不能用来种草.并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边. John想知道,如果不考虑草地的总块数

HDU5117 Fluorescent 期望 计数 状压dp 动态规划

原文链接https://www.cnblogs.com/zhouzhendong/p/HDU5117.html 题目传送门 - HDU5117 题意 $T$ 组数据. 给你 $n$ 盏灯 ,$m$ 个开关,每一个开关对应的控制一些灯.所有可以控制某盏灯的开关被按了奇数次,那么这盏灯最终是亮着的,否则是不亮的. 现在每一个开关都可以选择按或者不按.我们称对于所有开关都做出 按或者不按 的一种选择 为一种 方案.一种方案的价值是其最终情况下灯数 $x$ 的三次方,即 $x^3$ . 求所有方案的价值

[bzoj3717][PA2014]Pakowanie_动态规划_状压dp

Pakowanie bzoj-3717 PA-2014 题目大意:给你n个物品m个包,物品有体积包有容量,问装下这些物品最少用几个包. 注释:$1\le n\le 24$,$1\le m\le 100$ 想法:以为是什么超级牛逼的背包dp,结果就是状压dp 状态:f[s]表示装s状态的物品需要多少背包,g[s]表示在f[s]的前提下,最大的背包剩余的容量. 转移:直接判断最后一个能不能装下当前物品,转移即可. 还有就是这个题卡常,只能直接用Lowbit枚举1,不能全枚举,会T... ... 最后

[bzoj1879][Sdoi2009]Bill的挑战_动态规划_状压dp

Bill的挑战 bzoj-1879 Sdoi-2009 题目大意: 注释:$1\le t \le 5$,$1\le m \le 15$,$1\le length \le 50$. 想法: 又是一个看数据范围想做法的题,我们想到状压dp. 看了题解... ...网上给的状态是f[len][s]表示长度为len满足状态s的字符串个数. 光看状态... ...可能算重啊?! 其实... ... 状态:dp[len][s]表示长度为len,能且只能满足状态为s的字符串个数. 转移:我们先预处理出g[i]

FZU 1025 状压dp 摆砖块

云峰菌曾经提到过的黄老师过去讲课时的摆砖块 那时百度了一下题目 想了想并没有想好怎么dp 就扔了 这两天想补动态规划知识 就去FZU做专题 然后又碰到了 就认真的想并且去做了 dp思想都在代码注释里 思想是很好想的..唯一的难点大概是 c++里面没有同或这种东西 得自己写 而我又不怎么会位运算 问了蕾姐半天也没搞懂怎么用~这个取反符号 到最后怒而手写了函数 一开始想的是 init后 输入nm都可以秒出 但是在使用~的路途上 发现至少我的方法 做这个题 不能做到init后随便输入 因为 每行 都有

状压DP初探&#183;总结

2018过农历新年这几天,学了一下状态压缩动态规划,现在先总结一下.   状态压缩其实是一种并没有改变dp本质的优化方法,阶段还是要照分,状态还是老样子,决策依旧要做,转移方程还是得列,最优还是最优,无后还是无后,所以它比较好理解.   状压,顾名思义就是要将一些状压想办法压缩起来(可以压,也可以删).其中这些状态都满足相似性和数量很多.这样才好压而且压得有意义.常见于一般的方格题,网络题等等.   所以一般基础的状压就是将一行的状态压成一个数,这个数的二进制形式反映了这一行的情况.所以位运算可

第一次接触状压DP

状压DP入门及理解 *(另类的暴力)*      一般状态数不多的时候就会开数组,但是有的状态并不好表示,于是,状压DP就产生了.     状压DP应该是分两类的,一类是压缩状态,另一类是舍弃状态.    我感觉初学状压DP难就难在二进制运算的应用,了解二进制运算符就显得十分重要.     所以我们先看下表,如果有不会二进制简单应用的请点击https://blog.csdn.net/sinat_35121480/article/details/53510793(神犇请忽略...) 下面就可以看题

[转]状态压缩dp(状压dp)

状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴. 为了更好的理解状压dp,首先介绍位运算相关的知识. 1.'&'符号,x&y,会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值.例如3(11)&2(10)=2(10). 2.'|'符号,x|y,会将两个十进制数在二进制下进行或运算,然后返回其十进制下的值.例如3(11)|2(10)=3(11). 3.'^'符号,x^y

『公交线路 状压dp 矩阵乘法加速』

公交线路 Description 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路: 1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站. 2.每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过). 3.公交车只能从编号较小的站台驶往编号较大的站台. 4.一辆公交车经过的相邻两个 站台间距离不得超过Pkm. 在最终设计