题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=2662

1.引言:用dp解决一个问题的时候很重要的一环就是状态的表示,一般来说,一个数组即可保存状态。

但是有这样的一类题目,它们具有dp问题的特性,但状态中所包含的信息过多,如果要用数组来保存状态的话需要四维以上的数组。

于是,就要通过状态压缩来保存状态,而使用状态压缩来保存状态的dp就叫做状态压缩dp。

2.状态压缩dp的特点:状态中的某一维会比较小,一般不会超过15,多了的话状态数会急剧上升而无法压缩,一般来说需要状态压缩的也就是这一维。

3.状态压缩dp常见优化:预处理是最常见的优化,尤其是在棋盘类问题上,如果我们想进一步提高效率, 我们还可以预处理出状态之间是否可以转移而不用在每一次转移中断。

tips:注意灵活运用位运算。

下面来说这道题:

题意:有一个n*m的棋盘(n,m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻(每个棋子最多和周围4个棋子相邻)。求合法的方案总数。

思路:对于每一行,如果把没有棋子的地方记为0,有棋子的地方记为1,那么每一行的状态都可以表示成一个2进制数,进而将其转化成10进制。

     那么这个问题的状态转移方程就变成了:

     设dp[i][j][k]表示当前到达第i行,一共使用了j个棋子,且当前行的状态在压缩之后的十进制数为k时的状态总数。那么我们也可以类似的写出状态转移方程:

   dp[i][i][k]=sum(dp[i-1][j-num(k)][w])   num(k)表示 k状态中棋子的个数,w表示前一行的状态。

最基本的做法是:首先判断k状态是否合法,也就是判断在这一行中是否有2个旗子相邻,然后枚举上一行的状态w,判断w状态是否合法,

           然后判断k状态和w状态上下之间是否有相邻的棋子。

   当然这样做的时间复杂度是很高的,也就是说有很多地方可以优化,比如:判断每一行状态是否合法,可以在程序一开始判断然后保存结果,判断k状态和w状态上下之间否    有相邻的棋子,可以利用位运算,if(k&w)说明上下之间有相邻的棋子。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
using namespace std; ll dp[][][<<];///dp[i][j][x]第i行放了j个棋子当前状态为x时的方法数
ll mark[<<];///十进制标记每一行的状态
ll ans,len; ll num(ll x) ///记录状态x中1的个数
{
ll sum=;
while(x)
{
if(x&)sum++;
x=x>>;
}
return sum;
} bool judge(ll x) ///判断状态x是否有相邻的棋子放在一起
{
if(x&(x<<)) return false;
return true;
} int main()
{
//freopen("in.txt","r",stdin);
int n,m,k;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
memset(dp,,sizeof(dp));
memset(mark,,sizeof(mark));
len=,ans=;
if(m>n) swap(n,m); for(ll i=; i<(<<m); i++) ///初始化第一行的放置方法数//剔除不合法状态(所谓的预处理)
{
if(judge(i)) ///若i状态没有相邻的棋子放在一起
{
dp[][num(i)][len]=;///则第一行状态为len(i)时1的个数为num(i)时的方法数
mark[len++]=i;///标记状态
}
} for(ll i=; i<=n; i++) ///第二行到第n行
{
for(ll j=; j<=k; j++) ///对于放0***n个棋子
{
for(ll x=; x<len; x++) ///对于0***len-1个状态(第i行)//枚举
{
for(ll y=; y<len; y++)///对于0***len-1个状态(第i-1行)//枚举
{
ll tmp=num(mark[x]);///第i行状态x中1的个数
if(((mark[x]&mark[y])==) && j>=tmp) ///若上下2行没相邻的且当前的棋子数目大于此行当前状态所用的棋子
dp[i][j][x]+=dp[i-][j-tmp][y];///放法数可相加
///到当前行共用了j个棋子,当前行用了tmp个棋子,状态为x,到上一行共用了j-tmp个棋子,状态为y
}
}
}
}
for(ll i=; i<len; i++) ///枚举状态相加
ans+=dp[n][k][i];
printf("%lld\n",ans);
}
return ;
}

代码:  //0.07s

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int maxn=(<<)+;
const int INF=0x3f3f3f3f; ll dp[][][maxn]; ///第i行用j个棋子的k状态能否达到
int mark[maxn]; int judge(int x)
{
if(x&(x<<)) return ;
return ;
} int num(int x)
{
int sum=;
while(x)
{
if(x&) sum++;
x=(x>>);
}
return sum;
} int main()
{
//freopen("in.txt","r",stdin);
int n,m,k;
while(scanf("%d%d%d",&n,&m,&k)==)
{
memset(dp,,sizeof(dp));
memset(mark,,sizeof(mark));
int len=;
if(m>n) swap(n,m);
for(int i=; i<(<<m); i++)
{
if(judge(i))
{
dp[][num(i)][len]=;
mark[len++]=i;
}
} for(int i=; i<=n; i++)
for(int j=; j<=k; j++)
for(int x=; x<len; x++) ///当前行
for(int y=; y<len; y++) ///当前行的前一行
if(((mark[x]&mark[y])==) && j>=num(mark[x]) )
dp[i][j][x]+=dp[i-][j-num(mark[x])][y]; ll ans=;
for(int i=; i<len; i++)
ans+=dp[n][k][i];
printf("%lld\n",ans);
}
return ;
}

hoj 2662 经典状压dp // MyFirst 状压dp的更多相关文章

  1. 树形DP和状压DP和背包DP

    树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...

  2. 【刷题笔记】DP优化-状压

    因为篇幅太长翻着麻烦,计划把DP拆成几个小专题,这里原文只留下状压,其他请至后续博文. 状态压缩优化 所谓状态压缩,就是将原本需要很多很多维来描述,甚至暴力根本描述不清的状态压缩成一维来描述. 时间复 ...

  3. 状压dp(状态压缩&&dp结合)学习笔记(持续更新)

    嗯,作为一只蒟蒻,今天再次学习了状压dp(学习借鉴的博客) 但是,依旧懵逼·································· 这篇学习笔记是我个人对于状压dp的理解,如果有什么不对的 ...

  4. dp乱写1:状态压缩dp(状压dp)炮兵阵地

    https://www.luogu.org/problem/show?pid=2704 题意: 炮兵在地图上的摆放位子只能在平地('P') 炮兵可以攻击上下左右各两格的格子: 而高原('H')上炮兵能 ...

  5. [Poj2411]Mondriaan's Dream(状压dp)(插头dp)

    Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 18096   Accepted: 103 ...

  6. poj2411 Mondriaan's Dream (轮廓线dp、状压dp)

    Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17203   Accepted: 991 ...

  7. POJ 1185 炮兵阵地 (状压dp)(棋盘dp)

    这题和poj 3254很像,但是更复杂了一些 都属于棋盘里放东西,然后又各种各样的限制,然后求方案或者最大值 (1)上一道题距离要大于1,这道题是大于2.所以判断的时候变成 !(x & (x ...

  8. Luogu2435 染色【状压qwq】【轮廓线DP】

    LINK 题目大意 有一个 n 行 m 列的格点图,你需要给每个点上染上 k 种颜色中的一种,要求没有两个相邻点颜色相同.给定第一行与最后一行的染色,试求总染色方案数. 思路 暴力预处理状态暴力转移可 ...

  9. Codeforces 678E. Another Sith Tournament(概率DP,状压)

    Codeforces 678E. Another Sith Tournament 题意: n(n<=18)个人打擂台赛,给定任意两人对决的胜负概率,比赛规则:可指定一人作为最开始的擂主,每次可指 ...

随机推荐

  1. 3.1、Spring和Struts2的结合使用

    一.配置Struts2: 1.新建一个web项目,在src目录下新建com.st.bean/dao/service/action包,并在该包下面添加相应的接口及接口的实现类: a).在bean下新建一 ...

  2. codevs1257 打砖块

    题目描述 Description 在一个凹槽中放置了n层砖块,最上面的一层有n块砖,第二层有n-1块,--最下面一层仅有一块砖.第i层的砖块从左至右编号为1,2,--i,第i层的第j块砖有一个价值a[ ...

  3. mybatis问题is not known to the MapperRegistry

    is not known to the MapperRegistry mapper.xml里面对应的dao应该是interface而非class

  4. 【新手出发】从搭虚拟机开始,一步一步在CentOS上跑起来.Net Core程序

    文章背景 微软6月26号发布core 1.0版本后,园子里关于这方面的文章就更加火爆了,不管是从文章数量还是大家互动的热情来看,绝对是最热门的技术NO.1.我从去年底开始接触.net core到现在也 ...

  5. RxJava简单的介绍

    1. RxJava简介 Rx(ReactiveX,响应式编程)是一种事件驱动的基于异步数据流的编程模式,整个数据流就像一条河流,它可以被观测(监听),过滤,操控或者与其他数据流合并为一条新的数据流.而 ...

  6. touch事件中的touches、targetTouches和changedTouches详解

    touches: 当前屏幕上所有触摸点的列表; targetTouches: 当前对象上所有触摸点的列表; changedTouches: 涉及当前(引发)事件的触摸点的列表 通过一个例子来区分一下触 ...

  7. CString用法小结《转载》

    http://blog.sina.com.cn/s/blog_a674ea930101aeey.html

  8. 编写高质量代码:改善Java程序的建议

    建议的采用顺序是List<T>.List<?>.List<Object> List<T>.List<?>.List<Object> ...

  9. PHP 查看安装信息

    1.运行PHP脚本,查看phpinfo函数的输出. 2.在系统环境变量Path中配好php.exe可执行文件的路径,命令管理器CMD中,执行`php.exe -i`查看. 3.在phpinfo()的输 ...

  10. 【TIP】已经上架的app在AppStore上搜不到的解决办法

    相信很多人都遇到过这个问题,天天刷iTunes connect,终于发现app已经上架了,兴奋的跑过去告诉老板,老板说好,大家都装一个吧!然后大家过来问你怎么搜不到,老板看你都是一副这个表情:   这 ...