状态压缩dp相关

状态压缩dp

状态压缩是设计dp状态的一种方式。
当普通的dp状态维数很多(或者说维数与输入数据有关),但每一维总
量很少是,可以将多维状态压缩为一维来记录。
这种题目最明显的特征就是: 都存在某一给定信息的范围非常小(在20
以内),而我们在dp中所谓压缩的就是这一信息。
(或者是在做题过程中分析出了某一信息种类数很少)
我们来看个例子

经典题

给出一个n*m的棋盘,要放上一些棋子,要求不能有任意两个棋子相邻。
求方案数。
n<=100;
m<=8。

\(dp[i][s]\) 表示第i行的状态 s是这一行棋子的排列压缩而成的二进制数,1表示有棋子,0表示没有棋子;

转移:\(dp[i][s]=\sum_{s'}dp[i-1][s']|s \& s'=0\)

我们发现这个m是非常小的,这样就可以启发我们对每一行2^m状态压缩。
设dp[i][S]表示到了第i行,第i行的状态是S的方案数是多少。
其中S的第j位为1,表示i这行第j位放了一个棋子。
状态转移:$ dp[i][S]=\sum_{S&S'==0}{dp[i-1][ S' ]}$。
你会发现这样记录很暴力,状态数是与m相关的指数级的,但同时也就是因为m小我们就确实可以这么做。
这也正好映照了之前的:

其实本质就是很暴力的记录状态,只不过利用了题目本身的特殊条件(这一维很小),使得我们并不会因此复杂度过高。
同时也就是说,如果题目本身没有这样一个较小的信息,就不能应用状态压缩。
所以在接下来的题目中大家可以更注意一下题目所给的条件。
状态压缩dp肯定是有一维是指数级的,这正是状态压缩的特点。

BZOJ1087 互不侵犯

在n*n的棋盘上放置k个国王,使得任意两个国王互相不攻击。一个国王可以攻击到周围八个格子。
求放置方案数。
n<=9

\(dp[i][j][s]\)表示第i行s状态,之前放了j个国王的方案数;

\(dp[i][j+cnt(s)][s]=\sum_{(s'|(s'<<1)|(s'>>1))\&s==0}dp[i-1][j][s']\)

状态s与状态s'的转移:进行左移和右移。&3次,分别为不变,左移,右移;

更简单的思路(s|(s<<1)|(s>>1))&s'==0;

时间复杂度$O(n^3状态数^2)<O(n^32^(2n)) $

位运算

1、 (s & (1 << i)):判断第 i 位是否是 1;
2、 s =s|(1<<i):把第 i 位设置成 1;
3、 s =s&(~(1<<i)):把第 i 位设置成 0;
(~:是按位取反,包括符号位)
4、 s =s^(1<<i) :把第 i 位的值取反;
5、 s =s & (s – 1):把一个数字 s 二进制下最靠右的第一个 1 去掉;
6、 for (s0 = s; s0; s0 = (s0 - 1) & s): 依次枚举 s 的子集;

复杂度: 对于一个共有n位有k个1的二进制数s,它的子集的个数\(C_n^k\)

那么对于1~n的所有情况:ans=\(\sum\limits_{k=0}^nC_n^k*2^k*1^{n-k}=(1+2)^n\)二项式定理

\(2^k\)表示k个1的所有可能情况

7: x&-x 取出x最低位的1。

拓扑序个数问题

给你一张拓扑图,求这张拓扑图有多少种不同的拓扑序.
n<=20。

dp[s]表示当前s集合中的点都已经在拓扑序中的方案数

转移考虑枚举下一个点选什么,下一个选的点要满足它在s中的点选完后的入度为0,也就是指向它的点都已经加进拓扑序(状态s)里了,转移到dp[s|(1<<i-1)] 。

处理一个数组in[u]表示其他哪个点指向u,然后转移的话就是保证s&in[u]=in[u];

bzoj2734

给定n,问集合{1,2,3,…,n}有多少个子集满足:若x在集合内,则2x和3x不在集合内。
n<=100000

\(q*2^x*2^y\)

记g[x]表示将x中的2,3因子去除后得到的值 (即q),记g[x]表示将x中的2,3因子去除后得到的值,若g[x]!=g[y],那么x与y互不影响。对于互相有影响的一组数,一定能表示成q2^a^ 3^b^(q为常数)。对每种q分别求解,再相乘即可。

打表:

\(1 \ \ \ \ \ \ 3^1 \ \ \ \ \ \ \ 3^2 \ \ \ \ \ \ \ 3^3\)

\(2^1 \ \ \ \ \ 2^1*3^1 \ \ \ \ \ 2^1*3^2 \ \ \ \ \ 2^13^3\)

\(2^2 \ \ \ \ \ 2^2*3^1 \ \ \ \ \ 2^2*3^2 \ \ \ \ \ 2^2*3^3\)

\(2^3 \ \ \ \ \ 2^3*3^1 \ \ \ \ \ 2^3*3^2 \ \ \ \ \ 2^3*3^3\)

显然向右和向下会冲突,也就是右和下会互相冲突

时间复杂度分析:一行一行来看,因为最大n<=100000 ,那我们3最多大约是3^10^,因此共有11个状态0~10;所以s开11位?就可以了,时间复杂度约为f[11]*f[10]其中f[i]表示斐波那契数列第i项,转移啥的和t1很像。

NOIP2016 愤怒的小鸟

平面上有n头猪,每次可以从(0,0)出发发射一只沿抛物线(y=ax^2^+bx)飞行的小鸟,可以消灭所有在飞行路线上的猪。
问消灭所有猪至少要几只小鸟。
n<=18

两头猪加上原点即可确定抛物线,于是不同的抛物线只有O(n^2)种。

start:

确定抛物线:

猪的数量不是很多,每一条抛物线至少要打到一只猪,所以可以枚举出所有可行的抛物线,这条抛物线对每一只猪能/不能打下来用一个二进制为表示。

当然只能打到1只猪的抛物线不用枚举,一开始就加进去它的表示就行。

枚举两只猪,如果它们不在同一列,则将它们的坐标x,y分别代入方程y=ax^2^+bx,解得a,b,如果这组a,b没找到过且a为负,则用这组a,b尝试对每一只猪尝试看能不能打到(计算横坐标对应纵坐标)。

假设当前状态为i,抛物线为第j条,抛物线打掉的小猪状态为para[j],那么有:

\(dp[i|para[j]]=min(dp[i|para[j]],dp[i]+1)\)

end.

start‘

设f[S]为已经消灭的猪的集合为S时的最少次数,暴力的转移方法为依次枚举抛物线去更新所有f,这样做时间复杂度为O(n^2^*2^n^)。

更快的转移方法为从小到大枚举S,每次打掉编号最小的还没消灭的猪,由于包含该猪的抛物线只有O(n)种,所以时间复杂度为O(n*2^n^)。

第i个抛物线:t[i]表示这条抛物线能消灭的猪是哪几只

dp[s]集合s中所有点都被消灭的最少抛物线条数;

在打0只小猪的时候,需要用0只小鸟,于是有:

dp[0]=0

dp[s]=min~i~{dp[s^t[i]]+1|t[i]&s=t[i],且t[i]包含s最低位}

首先s^t[i]表示没打这一坨小猪时的最小值,然后因为上面为了更快的算出来,所以我们要保证每次都要打掉编号最小的猪,然后找到所有状态中最小的吖+1,就是dp[s]的值w;

poj2923

有 n 个货物,并且知道了每个货物的重量,每次用载重量分别为c1,c2的火车装载,问最少需要运送多少次可以将货物运完。
N<=17

dp[s]表示s集合被c1,c2运走至少需要几次;

先处理出可以一次运走的情况,然后转移, 枚举子集:

dp[s]=min~t~{dp[s^t]+1|t&s=t};

找出所有状态(1.....(1<<n)-1)中选出可以用两辆车一次运完的状态(枚举全部)

把每个状态都看作一个物品,重量为该状态的总重量,价值为 1,其实我们就是求最少选几个不相交的集合能够覆盖全集。
求解 01 背包, dp[(1<<n)-1]为最优解。

转移方程: dp[s]=min(dp[s],dp[s^t]+1) 其中t集合内的元素可以被一次运完。
这个复杂度是O(3^n)。
实际上如果我们固定t集合包含s最低位的1,这样能避免重复运算,复杂度降约为O(3^(n-1))

Bzoj3900

动物园里有 n 头麋鹿。每头麋鹿有两支茸角,每支茸角有一个重量。然而,一旦某头麋鹿上两支茸角的重量之差过大,这头麋鹿就会失去平衡摔倒。为了不然这种悲剧发生,动物园院长决定交换某些茸角,使得任意一头麋鹿的两角重量差不超过 c。然而,交换两支茸角十分麻烦,不仅因为茸角需要多个人来搬运,而且会给麋鹿造成痛苦。因此,你需要计算出最少交换次数,使得任意一头麋鹿的两角重量差不超过 c。
注意,交换两支茸角只能在两头麋鹿之间进行。因为交换同一头麋鹿的两支角是没有意义的。
对于 100% 的数据, n <= 16, c <= 1000000, 每支茸角重量不超过1000000。

将所有鹿角排序,然后相邻两个做匹配,看相邻两个是否相差<=c;

设f[s]表示s这个集合的麋鹿,最少交换多少次可以使其合法;上界:f[s]=(cnt(s)-1)or inf;

f[s]=min~s'∈s~{f[s']+f[s^s']};​

Hdu3001放松题

现在有一个具有n个顶点和m条边的无向图(每条边都有一个距离权值),小明可以从任意的顶点出发,他想走过所有的顶点而且要求走的总距离最小,并且他要求过程中走过任何一个点的次数不超过2次。
1<=n<=10

\(dp[s][u]\)当前在u点,s状态第j位为0没有经过,为1经过1次,为2经过两次;

枚举下一个点v走哪里;

\(dp[s+(1<<v)][v]=min \{dp[s][u]+len[u][v]\}\)

min \(dp[s][u]\),u=1~n

答案就是\(dp[3^n-1][i]\)的最小值

BZOJ3717 Pakowanie

给出n个物品和m个包,物品有各自的体积,包有各自的容量。
问将所有物品装入包中需要至少几个包。
◦ n<=24, m<=100。

贪心来看显然要用尽量用最大的那几个, 所以先把包从大到小排序。

设f[S]为把集合S放入包后至少要用到第几个包 ,g[S]记录此时用到的最后一个包能剩余的最大体积。

转移时枚举接下来放哪个物品即可。
时间复杂度O(n*2^n)

只是口胡rwr,你让我写代码我一个都写不出来

猜你喜欢

转载自www.cnblogs.com/zhuier-xquan/p/11323441.html
今日推荐