• 状态压缩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^3*2^(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=(sumlimits_{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互不影响。对于互相有影响的一组数,一定能表示成q*2^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,你让我写代码我一个都写不出来

  • 相关阅读:
    spring aop
    spring Mybatis集成
    springDao的jdbctemplate
    spark介绍4(sparksql)ODBC(Windows)gc
    ssm集合的配置
    scala文件流操作,生成sparkpv,uv作业文件
    java数据库操作
    vue一键打包部署
    Vue 那些事
    CDN合集
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11323441.html
Copyright © 2020-2023  润新知