dp状态压缩
dp状态压缩
动态规划本来就很抽象,状态的设定和状态的转移都不好把握,而状态压缩的动态规划解决的就是那种状态很多,不容易用一般的方法表示的动态规划问题,这个就更加的难于把握了。难点在于以下几个方面:状态怎么压缩?压缩后怎么表示?怎么转移?是否具有最优子结构?是否满足后效性?涉及到一些位运算的操作,虽然比较抽象,但本质还是动态规划。找准动态规划几个方面的问题,深刻理解动态规划的原理,开动脑筋思考问题。这才是掌握动态规划的关键。
动态规划最关键的要处理的问题就是位运算的操作,容易出错,状态的设计也直接决定了程序的效率,或者代码长短。状态转移方程一定要仔细推敲,不可一带而过,要思考为什么这么做,掌握一个套路,遇见这类问题能快速的识别出问题的本质,找出状态转移方程和DP的边界条件。
下面两个题目:
poj3254 Corn Fields
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 16473 | Accepted: 8678 |
Description
Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.
Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.
Input
Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)
Output
Sample Input
2 3
1 1 1
0 1 0
Sample Output
9
Hint
1 2 3
4
There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.
Source
大致题意:
给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。
分析:
假如我们知道第 i-1 行的所有的可以放的情况,那么对于第 i 行的可以放的一种情况,我们只要判断它和 i - 1 行的所有情况的能不能满足题目的所有牛不相邻,如果有种中满足,那么对于 i 行的这一中情况有 x 中放法。
前面分析可知满足子状态,我们我们确定可以用dp来解决。
但是我们又发现,状态是一种放法,不是我们平常dp的简单的状态,所以要用状态压缩!
但是什么是状态压缩呢?
比如前面的情况,一种放法是最多由12个 0 或者 1 组成的,那么我们很容易想到用二进制,用二进制的一个数来表示一种放法。
定义状态dp【i】【j】,第 i 行状态为 j 的时候放牛的种数。j 的话我们转化成二进制,从低位到高位依次 1 表示放牛0表示没有放牛,就可以表示一行所有的情况。
那么转移方程 dp【i】【j】=sum(dp【i-1】【k】)
状态压缩dp关键是处理好位运算。
这个题目用到了 & 这个运算符。
用 x & (x<<1)来判断一个数相邻两位是不是同时为1,假如同时为 1 则返回一个值,否则返回 0 ,这样就能优化掉一些状态
用 x & y 的布尔值来判断相同为是不是同时为1。
#include <cstdio>
#include <cstring>
const int N = ;
const int M = <<N;
const int mod = ;
int st[M],map[M]; ///分别存每一行的状态和给出地的状态
int dp[N][M]; //表示在第i行状态为j时候可以放牛的种数
bool judge1(int x) //判断二进制有没有相邻的1
{
return (x&(x<<));
}
bool judge2(int i,int x)
{
return (map[i]&st[x]);
}
int main()
{
int n,m,x;
while(~scanf("%d%d",&n,&m))
{
memset(st,,sizeof(st));
memset(map,,sizeof(map));
memset(dp,,sizeof(dp));
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++){
scanf("%d",&x);
if(x==)
map[i]+=(<<(j-));
} }
int k=;
for(int i=;i<(<<m);i++){
if(!judge1(i))
st[k++]=i;
}
for(int i=;i<k;i++)
{
if(!judge2(,i))
dp[][i]=;
}
for(int i=;i<=n;i++)
{
for(int j=;j<k;j++)
{
if(judge2(i,j)) //判断第i行 假如按状态j放牛的话行不行。
continue;
for(int f=;f<k;f++)
{
if(judge2(i-,f)) //剪枝 判断上一行与其状态是否满足
continue;
if(!(st[j]&st[f]))
dp[i][j]+=dp[i-][f];
}
}
}
int ans=;
for(int i=;i<k;i++){
ans+=dp[n][i];
ans%=mod;
}
printf("%d\n",ans);
}
return ;
}
HOJ 2662
有一个n*m的棋盘(n、m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻(每个棋子最多和周围4个棋子相邻)。求合法的方案总数。
直接考虑解决这个问题并不容易,我们先来考虑这个问题的退化形式:
现在我们令n=1。则我们可以很容易的想到状态转移方程:
设dp[i][j][0]表示当前到达第i列,一共使用了j个旗子,且当前格子的状态为不放的状态总数,类似的 dp[i][j][1]就是当前格子的状态为放的状态总数。
那么状态转移方程就是
dp[i][j][0]=dp[i-1][j][1]+dp[i-1][j][0];
dp[i][j][1]=dp[i-1][j-1][0];
当n=1的时候这个问题无疑是非常简单的,但是如果我们想模仿这种做法来解决原问题的话,就会遇到这样的问题:如何来表示当前行的状态?
昨天已经提到了一些状态压缩的知识,如果看懂了的话,应该已经明白怎么做了。
对于每一行,如果把没有棋子的地方记为0,有棋子的地方记为1,那么每一行的状态都可以表示成一个2进制数,进而将其转化成10进制。
那么这个问题的状态转移方程就变成了
设dp[ i ] [ j ][k ]表示当前到达第i列,一共使用了j个棋子,且当前行的状态在压缩之后的十进制数为k 时的状态总数。那么我们也可以类似的写出状态转移方程:
dp[ i ][ j ][ k ]=sum( dp[ i-1][ j-num(k) ][ w ] ) num(k)表示 k状态中棋子的个数,w表示前一行的状态。
虽然写出了状态转移方程,但是还是有很多细节问题需要解决:比如,如何保证当前状态是合法的?
最基本的做法是:首先判断k状态是否合法,也就是判断在这一行中是否有2个旗子相邻,然后枚举上一行的状态w,判断w状态是否合法,然后判断k状态和w状态上下之间是否有相邻的棋子。
当然这样做的时间复杂度是很高的,也就是说有很多地方可以优化,比如:判断每一行状态是否合法,可以在程序一开始判断然后保存结果,判断k状态和w状态上下之间是否有相邻的棋子,可以利用位运算,if(k&w)说明上下之间有相邻的棋子等等。
讲到这里,这道题目的做法已经很明确了,请大家自行完成。
其中k`表示上一行的状态,num(k)表示在当前行状态为k的情况下棋子的总个数。
#include cstdio
#include cstring
#include algorithm
using namespace std; long long f[][<<][]; inline int getNum(int x)
{
int s=,tmp=;
while (x)
{
if (s && (x & )) return -;
if (s=(x & )) tmp++;
x=x>>;
}
return tmp;
} int main()
{
int n,m,t;
while (scanf("%d %d %d",&n,&m,&t)!=EOF)
{
if (n《m) swap(n,m);
memset(f,,sizeof(f));
f[][][]=;
for (int i=;i<=n;i++)
for (int r=;r<=t;r++)
for (int j=;j<(<<m);j++) // 当前的状态
{
int num=getNum(j); // 棋子个数
if (num==- || num>r) continue;
for (int k=;k<(<<m);k++) // 上次的状态转移
{
if (getNum(k)==- || k&j) continue;
f[i][j][r]+=f[i-][k][r-num];
}
}
long long ans=;
for (int i=;i<(<<m);i++) ans+=f[n][i][t];
printf("%lld\n",ans);
}
return ;
}
dp状态压缩的更多相关文章
- HDU 4336 Card Collector (期望DP+状态压缩 或者 状态压缩+容斥)
题意:有N(1<=N<=20)张卡片,每包中含有这些卡片的概率,每包至多一张卡片,可能没有卡片.求需要买多少包才能拿到所以的N张卡片,求次数的期望. 析:期望DP,是很容易看出来的,然后由 ...
- HDU 1074 Doing Homework (dp+状态压缩)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1074 题目大意:学生要完成各科作业, 给出各科老师给出交作业的期限和学生完成该科所需时间, 如果逾期一 ...
- hdu_4352_XHXJ's LIS(数位DP+状态压缩)
题目连接:hdu_4352_XHXJ's LIS 题意:这题花大篇篇幅来介绍电子科大的一个传奇学姐,最后几句话才是题意,这题意思就是给你一个LL范围内的区间,问你在这个区间内最长递增子序列长度恰为K的 ...
- hdu 4352 数位dp + 状态压缩
XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 【bzoj1076】[SCOI2008]奖励关 期望dp+状态压缩dp
题目描述 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再 ...
- hdu4336 Card Collector(概率DP,状态压缩)
In your childhood, do you crazy for collecting the beautiful cards in the snacks? They said that, fo ...
- 洛谷 1052 dp 状态压缩
洛谷1052 dp 状态压缩 传送门 (https://www.luogu.org/problem/show?pid=1052#sub) 做完这道题之后,感觉涨了好多见识,以前做的好多状压题目都是将一 ...
- NOIP2005过河[DP 状态压缩]
题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...
- ACM: HDU 5418 Victor and World - Floyd算法+dp状态压缩
HDU 5418 Victor and World Time Limit:2000MS Memory Limit:131072KB 64bit IO Format:%I64d & ...
随机推荐
- Android集成二维码扫描功能
文章转载自 https://github.com/yipianfengye/android-zxingLibrary 在具体介绍该扫描库之前我们先看一下其具体的使用方式,看看是不是几行代码就可以集成 ...
- 新浪云虚拟机ftp链接显示失败问题
新浪云虚拟机ftp链接显示失败问题 测试是在局域网遇到的 域名解析可以ping有字节回复 账号密码也没有错误,但是链接一直出现 连接失败 拒接连接等问题 解决办法: 其实是局域网内的问题,这 ...
- [Windows Server 2003] 服务器安全加固
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:服务器安全加固 ...
- 易买网之smartupload实现文件上传
经过俩个星期的奋斗,易买网项目完工.在之前,实现图片的上传,走过许多弯路,原来是好多基础的知识忘记了,没把smartupload文件包添加组件jar包至WEB-INF/lib包中,在此特别重视,做下文 ...
- CAD动态绘制带面积周长的圆(com接口)
CAD绘制图像的过程中,画圆的情况是非常常见的,用户可以在控件视区点取任意一点做为圆心,再动态点取半径绘制圆. 主要用到函数说明: _DMxDrawX::DrawCircle 绘制一个圆.详细说明如下 ...
- js-2018-11-01 关于break和continue语句
1.label语句 语法:label: statement 加标签语句一般都要与for语句等循环语句配合使用. 2.break语句 立即退出循环,强制执行循环后面的语句. 3.continue语句 立 ...
- linux下的vi的使用方法
vi的使用: 一般指令模式: vi打开一个文件就直接进入一般指令模式,可以进行删除.复制.粘贴.但是不可以对文件的内容进行修改. 常用命令: ctrl + f 向下移动一页 ctrl + b 向上移动 ...
- Problem 29
Problem 29 Consider all integer combinations of ab for 2 ≤ a ≤ 5 and 2 ≤ b ≤ 5: 仔细看看以下a与b的组合 22=4, 2 ...
- “从客户端(content="XXXX")中检测到有潜在危险的 Request.Form值” 解决方案
解决方案一: 在.aspx文件头中加: <%@Page validateRequest="false" %> 解决方案二: 修改web.config文件: <co ...
- selenium等待
简介 在selenium操作浏览器的过程中,每一次请求url,selenium都会等待页面加载完成以后, 才会将操作权限在交给我们的程序. 但是,由于ajax和各种JS代码的异步加载问题,当一个页面被 ...