学习笔记:状态压缩DP

我们知道,用DP解决一个问题的时候很重要的一环就是状态的表示,一般来说,一个数组即可保存状态。但是有这样的一些题 目,它们具有DP问题的特性,但是状态中所包含的信息过多,如果要用数组来保存状态的话需要四维以上的数组。于是,我们就需要通过状态压缩来保存状态,而 使用状态压缩来保存状态的DP就叫做状态压缩DP。

一道例题:

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)说明上下之间有相邻的棋子等等。

讲到这里,这道题目的做法已经很明确了,请大家自行完成。

另一道例题:

TSP问题

给你n个城市和城市之间的通路的长度,请你找出一条经过所有城市一次且仅经过一次的路线,使得这条路线的长度最短。

问题分析,如果要设计一个状态的话,显然状态与已经走过的城市和你当前所在的城市有关,现在,按照一定的顺序给每个城市一个编号,如果已经走过的城市记为1,没走过的城市记为0,那么已经走过的城市的状态就可以压缩成一个数。所以,该题目的状态表示为:

Dp[i][j]表示已经走过的城市为i,当前所在的城市为j的最短路程。

相应的状态转移方程为dp[ i ][ j]=min( dp[ i ^ (1<<j) ][ k ] + dis[ k ][ j ]  );     i ^ (1<<j)的意思是将j这个城市从i状态中去掉。 dis[ k][ j ] 是k和j之间的距离。

HOJ2665 虽然题意与之相去甚远,但是本质上是一样的,希望大家能够完成这道题目。

状态压缩DP的特点:

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

状态压缩DP的常见优化:

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

灵活运用位运算,例题1中if(k&w)就是一个很好的例子。

推荐题目:

除了上述2道题目以外,我还推荐:

HOJ

•   2188 WordStack

•   2798 Globulous gumdrops

•   2800 Artillery Assignment

这一类DP对于编码能力要求比较高,请大家尽力而为。

转自http://blog.csdn.net/lmyclever/article/details/6671923

时间: 2024-08-26 23:40:07

学习笔记:状态压缩DP的相关文章

Victor and World(spfa+状态压缩dp)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5418 Victor and World Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)Total Submission(s): 958    Accepted Submission(s): 431 Problem Description After trying hard fo

POJ--3311--Hie with the Pie(暴力枚举+floyd)/(状态压缩DP+floyd)

简介 状态压缩入门,先说用暴力枚举的方法做的,再说状态压缩DP,对于刚开始学习状态压缩的同学,两者相互比较学习,可以明显看出两者区别,有利于对状态压缩DP的理解,提前说下,两者耗时是 157Ms和 0Ms . 题意 一披萨店员送披萨,从披萨店开始送给N个客户会给你一个(N+1)*(N+1)的矩阵,对于矩阵 g[i][j] 表示 i 处到 j 处所耗费的时间,0 代表披萨店,每条路径可以重复走. 问:店员从披萨店开始送完所有的披萨再返回店中的最短耗时.注意,出店就拿有 N 个披萨,不必重复返回店里

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

HDU1565(状态压缩dp)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8170    Accepted Submission(s): 3095 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数

HDU 3001【状态压缩DP】

题意: 给n个点m条无向边. 要求每个点最多走两次,要访问所有的点给出要求路线中边的权值总和最小. 思路: 三进制状态压缩DP,0代表走了0次,1,2类推. 第一次弄三进制状态压缩DP,感觉重点是对数据的预处理,利用数组分解各个位数,从而达到类似二进制的目的. 然后就是状态的表示,dp[s][i]表示状态s时到达i的最优值. 状态转移也一目了然,不废话. #include<stdio.h> #include<string.h> #include<algorithm> u

poj 3311 Hie with the Pie(状态压缩dp)

Description The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait for 1 or more (up to 10) orders to be

HDU--1074(状态压缩DP)

典型的状态压缩DP,给出了每件作业的截止时间和花费,求让老师扣分最少的写作业方式.把完成n种作业用状态2^n-1表示,dp[s]表示 完成状态s时,最小扣分.比如“111”,那么可以由“011”,“110”,“101”转移过来,分别表示选了0,1号作业,1,2号作业,0,2号作业. t[s]表示状态S记录的总时间.dp[s] = min{dp[j]+c[k] - d[k]},其中j = i^(1<<k),0<k<n;pre[s]表示状态s完成时,最末尾完成的作业, #include

poj 3254 Corn Fields(状态压缩dp)

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

poj 2411 Mondriaan&#39;s Dream(状态压缩+dp)

 题意:用1*2砖块铺满n*m的房间. 思路转自:http://www.cnblogs.com/scau20110726/archive/2013/03/14/2960448.html 因为这道题输入范围在11*11之间,所以可以先打表直接输出.......... 状态压缩DP 经典覆盖问题,输入n和m表示一个n*m的矩形,用1*2的方块进行覆盖,不能重叠,不能越出矩形边界,问完全覆盖完整个矩形有多少种不同的方案 其中n和m均为奇数的话,矩形面积就是奇数,可知是不可能完全覆盖的.接着我们来看