zoukankan      html  css  js  c++  java
  • 清北学堂2019.8.8

    Day 3 赵和旭

    状态压缩dp

    状态压缩是设计dp状态的一种方式

    当普通的dp状态维数很多(或者说维数与输入数据有关),但每一维总量很少是,可以将多维状态压缩为一维来记录。

    这种题目最明显的特征就是:都存在某一给定信息的范围非常小(在20以内),而我们在dp中所谓压缩的就是这一信息。(或者是在做题过程中分析出了某一信息种类数很少)

    经典题

    给出一个n*m的棋盘,要放上一些棋子,要求不能有任意两个棋子相邻。

    求方案数。n<=100,m<=8

    通过dp[i][s] s的二进制表示如何放

    则相邻行可以表示dp[i][s’]

    满足性质时s&s'==0

    BZOJ1087 互不侵犯

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

    求放置方案数。n<=9

    f[i][k][S]表示k个放完前i行,第i行的放置状态为S的方案数。

    S为一个n位二进制数,表示第i行的每一位上是否有棋子。

    由于棋子不能相邻,也就是这个二进制不能有相邻的1,所以可以先对求出对一行来说合法的状态(合法的二进制数有哪些)。

    转移:f[i][k+cnt(S)][S]+=f[i-1][k][T]( (T|(T<<1)|(T>>1)) & S )==0 (巧用位运算!)

    时间复杂度O(n^3*状态数^2)<O(n^3*2^(2n))(实际上状态数是 Fibonacci

    位运算

    1、(s & (1 << i)):判断第 i 位是否是 1; 7: x&-x 取出x最低位的1

    2、s =s|(1<<i):把第 i 位设置成 1;

    3s =s&(~(1<<i)):把第 i 位设置成 0; (~:是按位取反,包括符号位)

    4s =s^(1<<i) :把第 i 位的值取反;

    5s =s & (s 1):把一个数字 s 二进制下最靠右的第一个 1 去掉;

    6for (s0 = s; s0; s0 = (s0 - 1) & s): 依次枚举 s 的子集;

    for (int s=1;s<(1<<n);s++)
        for (int s0=s;s0;s0=(s0-1)&s)

    复杂度O3^n

    拓扑序个数问题

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

    dp[s]表示当前s集合中的点都已经在拓扑序中的方案数,转移考虑枚举下一个点选什么,下一个选的点要满足它在s中的点选完后的入度为0,也就是指向它的点都已经加进拓扑序里了,转移到dp[s|(1<<i-1)] 。考虑位运算优化

    bzoj2734

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

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

    q=1为例,可把所有2^a*3^ba,b为行列画成一个三角形棋盘,求棋盘上放不能相邻的棋子的方案数。我们会发现这个三角形最宽的层最多只有11个,所以可以状态压缩。

    这也就对应了——或者是在做题过程中分析出了某一信息种类数很少。

    q>1,等价于q'=1&&n'=n/q

    剪枝:对于n/q相同的q,可以避免重复计算来加速。

    NOIP2016 愤怒的小鸟

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

    问消灭所有猪至少要几只小鸟。n<=18

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

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

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

    其实就避免一些重复的计算。

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

    poj2923

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

    找出所有状态(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)) 枚举子集3^n也是很常见的状压dp!!

    t[s]=|f[s’]&g[s^s’]

    Bzoj3900

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

    注意,交换两支茸角只能在两头麋鹿之间进行。因为交换同一头麋鹿的两支角是没有意义的

    首先其实最终肯定是把这些鹿分成一些组,每一组内通过组的大小减一次操作来满足题目要求的条件。注意对于一个组,我们将所有的角排序,第2*i-12*i个要保证之差小于等于C,才是合法的一组。

    其实就是选尽量多合法的组并起来等于全集,枚举子集的状态压缩dp即可。

    dp[i]=max{dp[j]+dp[i^j] | ji的一个子集}

    Hdu3001

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

    1<=n<=10

    dp[S][i]表示到过点的情况集合为S,S是个三进制数,第i0/1/2分别表示到过次数。i为当前所在的点。转移的话就考虑下一步走到那条边就好。

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

    会二进制的表示,三进制想起来应该很自然吧。

    其实是TSP旅行商问题的一种较为麻烦的形式。

    关于时间复杂度

    N=20一般是2^n或者n*2^n

    N<=16大概率是3^n,约是4*10^7,那很可能做法就和之前讲枚举子集有关了。

    N<=15大概率是3^n或者n*3^n

    BZOJ3717 Pakowanie

    给出n个物品和m个包,物品有各自的体积,包有各自的容量。

    问将所有物品装入包中需要至少几个包。

    n<=24m<=100

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

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

    转移时枚举接下来放哪个物品即可。

    时间复杂度O(n*2^n)

    DP优化方法

    逆序对

    求长度为n,逆序对为k的排列有多少个,答案对10^9+7取模。

    1≤n,k≤200

    1≤n,k≤2000

    排列题的一个套路,我们从小到大依次把数字插入到排列中,以这个思路进行dp

    这个问题设计动态规划的方法就是最基本,最自然的。

    我们设dp[i][j]表示插入了前i个数,产生的逆序对为j的排列的方案数,

    转移时就考虑i+1的那个数插在哪一个位置就好,因为它比之前的都要大,插在最后面就dp[i+1][j+0]+=dp[i][j],如果时最后一个数前面就是dp[i+1][j+1]+=dp[i][j],倒数第2个数前面就是dp[i+1][j+2]+=dp[i][j],依次类推。这个是从前向后更新。我们如果考虑dp[i][j]能从哪些状态转移过来,就可以前缀和优化。

    方程:dp[i][j]=∑dp[i-1][j-k] (0≤k<i)

    我们设:f[i][j]=∑dp[i][k] (0≤k<j)

    dp[i][j]=f[i-1][j]-f[i-1][j-i]

    这样我们通过记录前缀和,将转移优化成O(1)

    其实这题复杂度还可做到nk均是100000loj60772017年山东省队集训题)

    在这个dp基础上,做容斥原理,通过之前讲的整数划分的模型dp求出容斥系数即可。

    Bzoj1855:股票交易

    我们考虑在未来T天内的某只股票,第i天的买入价为每股APi,第i天的卖出价为每股BPi(对于每个i,都有APi>=BPi),每天不能无限制地交易,规定第i天至多只能购买ASi股,至多卖出BSi股。另外规定在两次交易(买入或者卖出均算交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。还规定在任何时间,一个人的手里的股票数不能超过MaxP

    初始w手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,问T天以后,小w最多能赚多少钱。

    0<=W<T<=2000

    f[i][j]表示到第i天手里持有j的股票的最大收益,那么第i天有三种操作:

    不买不卖:f[i][j]-f[i-1][j]

    买入:f[i][j]=f[i-w-1][k]-ap[i]*(j-k)|k≥j-as[i]

    卖出:f[i][j]=f[i-w-1][k]-bp[i]*(k-j)|k≤j+bs[i]

    对于买入,我们对其变形:

    f[i][j]=f[i-w-1][k]-ap[i]*(j-k)=f[i-w-1][k]+ap[i]*k-ap[i]*j

    那么可以用单调队列维护f[i-w-1][k]+ap[i]*k(因为对于固定的iap[i]是固定的),这样f[i][j]就能做到O(1)求得,而不必枚举k。卖出也一样。

    bzoj3831: [Poi2014]Little Bird

    有一排n棵树,第i棵树的高度是Di

    MHY要从第一棵树到第n棵树去找他的妹子玩。

    如果MHY在第i棵树,那么他可以跳到第i+1,i+2,...,i+k棵树。

    如果MHY跳到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。

    为了有体力和妹子玩,MHY要最小化劳累值。

    N(2<=N<=1000000)

    根据题意,我们很容易得出下面的转移方程

    1.f[i]=min(f[j]+1) ( i-kj<i )

    2.f[i]=min(f[j]) ( i-kj<i &&h[j]>h[i])

    发现上面那个东西用单调队列直接搞定,但下面那个不太好搞。不过发现由于h[j]>h[i]对答案的贡献至多为1,所以原来如果f[j]<f[j],那么算上h[j]h[j]的影响后j仍然不会比j’更差,于是直接维护一个f递增的单调队列,其中当f相同的时候使h递减就行了。

    这么做,主要利用了题目中高和矮转移的费用只差1,这样如果矮的f[i]小的话,就算加上1也只是和高的f值相同不会更差。如果费用更高就不能用这个方法了。

    送你一堆区间

    送你在数轴上的 n 个区间和 m 个关键点, 你可以决定每个区间选或不选, 问有多少种方案覆盖 所有的关键点. 1000000009取模.

    N<=5*10^5

    这三个技巧往往就是dp列出式子来之后,观察一下式子,你发现它满足对应优化的模型,所以我们就单调队列或者前缀和或者线段树优化了。

     Noip2008传纸条

    一个mn列的矩阵,而小渊和小轩传纸条。纸条从小渊坐标(1,1),传到小轩坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。班里每个同学都可以帮他们传递,但只会帮他们一次。全班每个同学有一个好心程度。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。

    n,m<=300

    其实可以理解为从小渊到小轩传两次。

    最暴力的做法:设dp[i][j][k][l]是第一张纸条到达(i,j),第二张到达(k,l)时最大权值。那么方程就是dp[i][j][k][l]=map[i][j]+map[k][l]+max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j -1][k][l -1]);

    还有一点注意的是,如果i == k && j == l,也就是第二张走了第一张的路径,那么就要减去,dp[i][j][k][l] -= map[i][j];

    接下来枚举每个i,j,k,l,最后输出dp[m][n][m][n]就行了,但这样做时间复杂度是O(n^4)

    其实我们可以让两个路线并行走,同时走。

    而既然第一张与第二张是同时走,那么我们知道他们的步数是一样的,步数 = 横坐标+纵坐标,所以只需枚举i,j,k,就能计算出l,只需三重循环,时间就变成了O(n^3);

    dp[i][j][k] = map[i][j] + map[k][i+j-k] + max(dp[i - 1][j][k -1],dp[i-1][j][k],dp[i][j-1][k-1],dp[i][j-1][k]);

     下午考试就比较有趣了,hhh <(︶︿︶)>_╭∩╮╭∩╮

  • 相关阅读:
    使用SELECT语句检索数据
    redis的安装和使用【2】redis的java操作
    Python之数据结构改造
    InnoDB undo log物理结构的初始化
    Redis-RDB持久化设置
    MySql(四)Select条件查询
    Node.js TLS/SSL
    Node.js 定时器
    Node.js 系统
    Node.js 字符串解码器
  • 原文地址:https://www.cnblogs.com/gongcheng456/p/11323551.html
Copyright © 2011-2022 走看看