Acwing算法提高课

DP问题

打卡第一天!

1.数字三角形模型(动态规划)

方法:从集合角度来考虑DP问题——y氏DP法

状态表示:集合(根据题目自己思考)and属性(数量/Max/Min)

状态计算:集合的划分

重要的划分依据:“最后”

重要的划分原则:1、不重复  2、不漏

摘花生问题

1015. 摘花生 - AcWing题库

 最低通行费问题

要注意边界的分类讨论和求最小值时,状态表示为正无穷。

活动 - AcWing

方格取数问题

用三维的数组表示,特殊状态k为步数,步数相同时,如果两条路线的横坐标+纵坐标相等,则两条路径的重合。k = i + j ,横纵坐标之和。

 1027. 方格取数 - AcWing题库

 传纸条问题

右下角往左上角传可以看作左上角往右下角传,转化为方格取数问题。

275. 传纸条 - AcWing题库

总结

题谱:

 

 90%的dp问题都能转化为最短路问题,拓扑图可以转化为dp问题。记住模型,到相似题目就会有更清晰的思路,不会到无从下手。

 2、最长上升子序列模型(LIS)

打卡第二天!

因为是一维的数组,所以状态表示只需要一维。

因为状态表示为以倒数第一个数结尾,所以考虑状态计算的时候,从倒数第二个数考虑(空的情况),是序列里只有a[i]的情况。

都有共同元素a[i],以a[k]结尾的最长上升子序列正好是f(k),所以等于f(k) + 1,且只有在a[k] < a[i]时成立。

最长上升子序列

基础的LIS模板。

895. 最长上升子序列 - AcWing题库

怪盗基德滑翔翼

往左:最长上升子序列。往右:最长下降子序列(逆向最长上升)

1017. 怪盗基德的滑翔翼 - AcWing题库

登山

状态表示时从顶点考虑,a[k]被算了两次,所以重复了,-1。

1014. 登山 - AcWing题库

合唱队形

登山问题的对偶问题。最少多少出列就是最多留下多少的意思,用总数减去结果即可。

482. 合唱队形 - AcWing题库

友好城市

控制住自变量,去思考因变量,散列,转化为一维问题。把岸两边的数都拍好序,确保连接的航道是友好航道,有一个不是上升的就会相交,因此是最长上升子序列模型。思考过程很难(能想到LIS,基本上就解决了)

 1012. 友好城市 - AcWing题库

 最大上升子序列和

比较简单,就是模型求个数和变成求值的和

1016. 最大上升子序列和 - AcWing题库

拦截导弹

DP + 贪心(基于基础课的最长上升子序列(II))

 1010. 拦截导弹 - AcWing题库

也可以用贪心 + 贪心来做,Lower_bond和upper_bond。

lower_bond : (begin , end , a )    返回第一个大于等于a的数组的下标

upper_bond : (begin , end , a)    返回第一个大于a的数组的下标

lower_bond : (begin , end , a , greater <int> () )    返回第一个小于等于a的数组的下标

upper_bond : (begin , end , a , greater<int> () )    返回第一个小于a的数组的下标

include <iostream>
#include <algorithm>
using namespace std;
const int N = 1100;
int f[N] , g[N];
int main()
{
    int len = 0 , cnt = 0;
    int a;
    while(cin >> a)
    {
        //pos1 表示以a结尾的最长下降子序列长度
        int pos1 = upper_bound(f , f + len , a , greater<int> ()) - f;
        if (pos1 == len)    f[len ++] = a;        //开创一套新的导弹系统
        else                f[pos1] = a;          //使a成为当前导弹系统的最后一位
        
        int pos2 = lower_bound(g , g + cnt , a) - g;
        if (pos2 == cnt)    g[cnt ++ ] = a;
        else                g[pos2] = a;
        
    }
    cout << len << endl << cnt << endl;
    return 0;
}

 导弹的防御系统

在拦截导弹的基础上使用全局最小值DFS,实现两种不同的拦截导弹系统的部属。

187. 导弹防御系统 - AcWing题库

最长公共上升子序列

最长公共子序列(4种)和最长上升子序列(从倒数第二个数考虑)结合考虑,用一个二维数组来表示集合。

根据状态计算可以用三重循环解决,复杂度是o(n^2)

最后一重循环也就是求1~j -1 的b序列的最长上升子序列。

但是最后一重循环在if的判断下,可以将b[j]替换成a[i],也就是跟k没有关系,因此可以优化成二重循环。  

272. 最长公共上升子序列 - AcWing题库

总结

题谱:

最长上升子序列可以转化变种为最长下降子序列,也可以结合贪心进行优化,还可以与最长公共子序列进行结合,最终都落实到用集合的划分。

3、背包模型

背包问题划分依据:物品作为一维,体积作为一维,互相约束,表示成集合。

所有问题都是01背包问题的拓展

01背包问题:每个物品只有选择或者不选

完全背包问题:每个物品可以选0、1、2....无数个

如果是这样的划分暴力搜索的话,需要遍历s个物品,算法复杂度会来到o(n^3),因此要考虑完全背包的优化。

另j = j - v,同时由于完全背包物体体积固定,所以s = j / v 是固定的(这点和多重背包不一样)。

可以惊奇的发现,上面的最大值就比下面的最大值多一个w。所以f[i , j]可以等于max(f[i - 1][j] , f[i][j - v] + w),省去一维的循环! 

优化过的完全背包模型:f[i][j] = max(f[i - 1][j] , f[i][j - v] + w

还可以把空间优化成一维。

注意:所有的背包问题都是先循环物品,然后体积,最后决策。

多重背包问题:每个物品可以选si个

如果是单纯的让j = j -v ,会发现会多出一项,不好直接表示。

注意:多重背包种的s是最多选s个,而完全背包的s是无上限,直到最大体积的。

所以最后一项为(j - v - sv) -> j - (s + 1)v,是这么来的。

多重背包的优化:用单调队列维护滑动串口 

 

r可以看作是拿完所有i物品后,剩下的体积余数,即r = j % v。

用g来维护总价值,q单调队列来维护体积。期间涉及滑动窗口的弹出,以及单调队列的去除冗余操作,代码不难,思路难想。

#include <iostream>
#include <cstring>
using namespace std;
int n , m;
const int N = 20100;
int g[N];       //g的滚动数组维护下标
int f[N];
int q[N];       //q的单调队列维护体积
int main()
{
    cin >> n >> m;
    for(int i = 0 ; i < n ; i ++ )
    {
        int v , w , s;
        cin >> v >> w >> s;
        memcpy (g , f , sizeof f);
        for(int j = 0 ; j < v ; j ++ )
        {
            int hh = 0 , tt = -1;
            for(int k = j ; k <= m ; k += v)
            {
                while(hh <= tt && q[hh] < k - s * v)     hh ++;          //维护滑动窗口
                while(hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j)/ v * w)   tt --;   // 去除冗余元素
                q[++ tt] = k;
                f[k] = g[q[hh]] + (k - q[hh]) / v * w;           //增加的收益
            }
        }
    }
    
    cout << f[m] << endl;
    return 0;
}

多重背包问题III(滑动窗口优化)

6. 多重背包问题 III - AcWing题库

背包背景的问题主要从三个状态考虑:物品,体积,价值,要从实际问题提取成这三个状态。

庆功会

多重背包问题,可用滑动窗口+滚动数组来优化实现

1019. 庆功会 - AcWing题库

分组背包问题:把物品分为若干组,每组物品选择一个决策

状态表示:前i组物品中选,总体积不超过j的方案。

状态计算:0表示不选第i组物品,结果为f[i - 1][j]

                  1表示选第i组物品中的第1个物品,结果为f[i -1][j - v[i , 1]] + w[i , 1]

                   2表示选第i组物品中的第2个物品,结果为f[i - 1][j - v[i , 2]] + w[i , 2]

                   ....

                  s[i]表示选第i组物品中的第s[i]个物品,结果为f[i - 1][j - v[i , s[i]] + w[i , s[i]] 

分组背包和其它背包的最本质区别:每组物品以及组内的每个物品对应的价值不是定值!! 

机器分配

把公司每个子公司的编号分成若干组,分配总数看作总体积,子公司分配数看作体积,每个体积对应一个价值。

要注意输入矩阵时,j不能从0开始,要和循环组的编号一致,从1开始

由于要输出具体方案,因此不能优化成一维。每组对应的体积用way数组来记录。

1013. 机器分配 - AcWing题库

金明的预算法案

把每个主件和附件的组合看成一个组。每个组合由多个选择,每个选择作为一种决策。

第一个比较麻烦的点是怎么把每个物品组存下来----- 可以用pair 和 vector来存储每个物品组。

注意选择主键的时候,要加上主键的价值。

二进制循环容器比较方便,记住二进制循环的两重循环+判断。

 487. 金明的预算方案 - AcWing题库

采药

简单的01背包一维空间优化。

423. 采药 - AcWing题库

装箱问题

将体积看成价值,用01背包解决。  

1024. 装箱问题 - AcWing题库

二维费用的背包问题

对于一个物品有两种对体积的限制,从状态表示的层面上修改状态计算。再用01背包的优化掉一维。

8. 二维费用的背包问题 - AcWing题库

宠物小精灵之收服(二维费用)

注意:体力值的下限不能为0,所以从1开始,体积2的最大值要-1。 

1022. 宠物小精灵之收服 - AcWing题库

潜水员

由最大体积变为最小体积,最大值变为最小值,从状态表示的方面上修改状态计算。

第三种题型:体积至少是j

初始化:

f[0][0][0] = 1          表示一个物品都不选,氧气,氮气至少为0,是一种方案

f[0][j][k] = INF       表示一个物品都不选,氧气,氮气至少为j,k。这是不可能的,由于求的 

                              是最小值,因此初始化为正无穷

注意:

j < vi 是,虽然j - v1 是负数,但是由于状态表示为至少,也就是方案下限存在,如果j-v1是负数的话,可以看作方案数为0,也就是另j = vi。跟其它题不同的就是不需要j >= vi,负数和不选气缸等价

三种情况的体积限制

凡是从i - 1层转移来的, 都是从大到小循环;从i层转移的,都是从小到大循环。

1020. 潜水员 - AcWing题库

数字组合

思考过程:

物品:每个数 , 体积:Ai , 价值:无,状态表示:所有从前i个物品中选择,总体积恰好是M的方案的总数count

不选物品的时候,体积为0,也是一种方案,因此在一维数组中,需要让f[0] = 1。

278. 数字组合 - AcWing题库

买书

完全背包模型,书的种类和价格是确定的,把n元钱看作体积,且体积恰好最大。

恰好的时候,注意初始状态的赋值:f[0] 也是一种方案,因此f[0] = 1

体积有四种,不需要输入,v[4]定义成全局变量

1023. 买书 - AcWing题库

开心的金明

简单的01背包模型。

426. 开心的金明 - AcWing题库

背包问题求具体方案

后面的方案由前面一层过来,因此记录状态的时候从后往前循环,注意状态计算中i - 1变成i + 1

由于判断是否被选的时候,要求按照字典序最小输出,因此输出i的时候,从前往后循环。 

 

货币系统

完全背包模型的求方案数。注意方案数很多,数组可能会越界,要用long long来存。

1021. 货币系统 - AcWing题库

货币系统(NOIP2018)

b集合中的数都是从a集合中来的。可以看成完全背包求方案数。

把a集合中每个数据看成物品,金额看成体积,每个数据可以选无限次,因此可以看错是完全背包。

把ai看作是最大体积,a1~ai - 1 的体积恰好等于ai的方案总数。

后一个数一定是比前一个数大的,因此要先排序。

注意:排序如果是从1开始,sort(a , a + n + 1) 要多加个1

 532. 货币系统 - AcWing题库

 混合背包问题

取决于第i件物品的类型,因此只需要考虑第i件物品。如果只有一个,用01背包的状态方程;如果有限个,用多重背包的状态方程;如果是无限个,用完全背包的状态方程。

因为数据过大,所以使用多重背包模型II(二进制优化)

也可以使用多重背包模型III(滑动窗口优化),亲测更快。

7. 混合背包问题 - AcWing题库

有依赖的背包问题

有依赖的背包问题 = dfs + 邻接表存储树 + 完全背包模型

背包问题求方案数(最大价值)

与最大体积限制不同,限制的是最大价值。

收到最短路算法的启发,可以将状态计算成如下图所示。

需要统计出最大值,如果最大值与当前状态相等,总数加上方案数

 11. 背包问题求方案数 - AcWing题库

能量石

神题,一定要吃透

先从贪心的角度来思考

如果从前往后来吃,得到的能量是Ei + Ei + 1 - Si*Li + 1

如果从后往前来吃,得到的能量是Ei + Ei + 1 - Si + 1 +  Li

如果要让解最优,就让Si*Li + 1 < Si + 1 +  Li 即可 ,得出Si/Li < Si + 1 / L i + 1

用背包考虑问题的时候,只需要从这个小集合考虑即可。小圈里一定包含最优解。

 这里的体积表示时间

不超过和恰好是j都可以,恰好是j比较好做。 

要排序的时候记得结构体下标从1开始,开头和结尾都要+1。

 734. 能量石 - AcWing题库

总结

要将题目转化为数量,体积,价值,体积可能有二维体积,价值可能没有;

            不超过,恰好,至少,对应状态初始化注意区别

 有些题目试着先用贪心的思想把集合范围缩小(复杂的问题精简化)

刷题图谱:

状态机模型

状态机的显著特点:不是表示一个点,而是表示一个过程。

 ​​​​​​

大盗阿福

 

 选了第i家店铺,那么第i - 1家就不能选。

这种思考方式有一个缺点:不知道i - 1是不是要选。如果第i - 1是最优解中的一组数据,那么选了i - 1之后,就不能选择i。这样只能从第i - 2层转移过来,效率不高,想要把状态从上一层转移过来。

因此,可以把每一层用状态表示出来

每一种走法对应一种边的走法。

0表示不选择,1表示选择。

状态机中有入口的概念,要初始化边界。入口只能走到0。所以f[0][0] = 0 , f[0][1] = -INF

 

 1049. 大盗阿福 - AcWing题库

股票买卖IV

f[i][j]表示第i天,正在进行第j次交易,从定义出发,一次买入卖出算作一次交易。(1 -> 0时,是卖出股票,表示第j次交易正在进行,是交易的一半,前面进行了j次交易,因此从j转移过来)(0 -> 1时,表示买入股票,表示第j次交易正在进行,前面进行了j - 1次交易,因此从j - 1转移过来)

 f[i][j][0] = max(f[i - 1][j][0] ,f[i - 1][j][1] + w[i])

 f[i][j][1] = max(f[i - 1][j][1] ,f[i - 1][j - 1][0] - w[i]) 

f[i][j][2] 表示第i天,正在进行第j次交易,有货为状态1,无货为状态0。一次买入卖出算做一次交易,最终一定是完整的交易,即停在无货状态0

1057. 股票买卖 IV - AcWing题库

股票买卖V

与股票IV的区别是,需要冷却一天,因此多了一个状态,无货的第一天是不能再买入的,只能转为无货的第>=2天的状态。

入口是最灵活的最后的状态,出口可能有两种情况。

由于没有限制交易的次数,因此不需要j这一维。

1058. 股票买卖 V - AcWing题库 

设计密码

KMP算法求出来的是所有包含的子串。不包含的子串,一定是到不了m这一跳的,即只需要求出j小于m中的kmp对应到不包含。

这本身是一个状态机模型,状态表示第一维是长度,第二维是在状态机上走到了哪个位置,这里是在枚举在位置j如果沿着k这条边走到u时,对f[i + 1, u]的方案数的贡献

1052. 设计密码 - AcWing题库

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值