zoukankan      html  css  js  c++  java
  • dp基础大概 (8.6)

     一些前言:

    据说动态规划会用排序,数据结构来进行乱搞优化操作

    动态规划滴核心是个啥呢?状态表示和状态转移

    设状态:哪些因素会影响到最终答案,就把哪些因素用数组的维度表示出来

    要充分描述,也要简洁

    举个例子

    计算从(1,1)走到(x,y)的方案数:

    走到任意一个(p,q),只能从(p-1,q)和(p,q-1)走过来

    那dp[x][y]=dp[x-1][y]+dp[x][y-1]

    例一:

    最长上升子序列

    这个比较简单

    dp[i]表示以a[i]为结尾的最长上升子序列的长度

    dp[i]=max{dp[j]}+1(a[j]<a[i]&&j<i)

    复杂度O(n2)

    更秀一点的O(nlogn)做法:

    len记录当前的最长上升子序列的长度,d[i]记录当前找到的最长上升子序列的第i项,若a[now]≤d[len],则找到第一个d[j]>a[now]的j,令d[j]=a[now],否则len++,d[len]=a[now],最后的len是最终答案,但d数组不一定是真正的最长上升子序列

    dp[i][j]表示前i个位置用j个乘号的最大值

    cnt(i,j)表示原数字串第i个数字到第j个数字所组成的数

    dp[i][j]=max(dp[i][j],dp[k][j-1]*cnt(k+1,i))

    挂饰:

    看起来像个贪心(大雾)

    那就排个序

    按照Ai从大到小排

    why?

    因为挂钩越多,能挂的东西就越多

    dp[i][j]=max(dp[i-1][j],dp[i-1][max(j-a[i],0)+1]+b[i])

    蓝字部分是挂第i个挂钩的喜悦值

    看蓝字部分的第二维,为什么+1要写在外面呢?

    ①:当j-a[i]+1<0时,j-a[i]肯定小于0,这时候考虑j-a[i]+1的状态是没有意义的,直接考虑手机上只有一个挂钩的情况

    ②:当j-a[i]+1==0时,那说明挂上i,就没有挂钩了,并且挂i只需要一个挂钩,也就是说之前手机上只有一个挂钩。如果我们写成max(j-a[i]+1,0),此时是转移到之前手机上没有挂钩的状态,是不对滴。

    综上,+1要写在外面

    最终答案:max{dp[n][i]}(0≤i≤n)

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    inline int read()
    {
        char ch=getchar();
        int x=0;bool f=0;
        while(ch<'0'||ch>'9')
        {
            if(ch=='-')f=1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return f?-x:x;
    }
    int n,dp[2010][2010];
    struct G{
        int a,b;
    }g[2010];
    bool cmp(G x,G y)
    {
        return x.a>y.a;
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
        g[i].a=read(),g[i].b=read();
        sort(g+1,g+1+n,cmp);
        memset(dp,0xcf,sizeof(dp));
        dp[0][1]=0;dp[0][0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
            {
                dp[i][j]=max(dp[i-1][j],dp[i-1][max(j-g[i].a,0)+1]+g[i].b);
            }
        }
        int ans=0xcfcfcfcf;
        for(int i=0;i<=n;i++)
         ans=max(ans,dp[n][i]);
        printf("%d",ans); 
    }
    代码

    (手机好沉ρωρ)

    洛谷P1233 木棍加工

    看起来像求最长不上升子序列个数,但是这似乎是二维的

    那我们按照l从大到小排序,再看w中有多少个不上升子序列的最少的数量

    那怎么求最少的不上升子序列的数量?

    dilworth定理(翻译成人话版本):

     最少的不上升子序列的数量就是最长上升子序列的长度(似乎在导弹拦截里面见过)

    why?

    度娘大概知道

    P1091合唱队形

     nlogn做法qwq(导弹拦截的做法)

    辣么另一种是什么呢?

    我们回顾求最长上升子序列的时候,我们要找最大的dp[j],使得a[j]<a[i]

    我们换个角度

    就是找最大的k,使得dp[j]==k&&j<i&&a[j]<a[i]

    很有可能有多个dp[j]==k

    我们设置辅助数组h,h[k]为dp[j]==k中,最小的a[j]

    同时h数组是单调递增的,所以查询时只要查询最大的小于a[i]的h数组的下标即可(二分查找)

    为什么是单调递增的?

    数据结构优化

    暴力的不要不要的

    据说是个二维偏序

    用树状数组搞

    维护前缀最大值

     如果要求公共子序列的个数怎么办

    dp[i][j]:第一个串的前i个字母,第二个串的前j个字母的公共子序列的个数

    若s[i]==t[j],选:转移到dp[i-1][j-1],不选:dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]  (这一块是重复的)

    若不相等:dp[i-1][j]+dp[i][j-1]

    数据范围你猜

    好吧是复杂度是n2

    先来个n4的暴力

    dp[i][j]:A的前i,B的前j的balabala

    然后搞一遍公共子序列的dp转移,如果小于末尾的数,就再找一遍

    唉唉这为毛是四维的??

    在算dp[i][j]时要看dp[1][1],dp[1][2]...dp[1][j-1],dp[2][1]...一直到dp[i-1][j-1]

    这样就是n2

    但是还是可以nb的继续优化

    我们再设f[j]=max{dp[x][j]}(1<=x<=i-1)

    当i到i+1的时候,只需要O(1)更改f[j]就可以了

    这样总体就是O(n2)了

    代码:

    a[i]>b[j]是个什么鬼咧?

    结合一下这张图。我们保证当前子序列的结尾是a[i],如果我们要更新后面的dp[i][j'],就是要求b[j']要小于a[i](当然,b[j]也要小于a[i])

    tmp又是个啥?

    tmp就是

    红色圈里这一坨的最大值

    好像似乎也许应该可能是一样的叭

     

    dp and 容斥

    zhx说小学学过(手动双关)

    什么是容斥?加加减减乱搞一通

    注意这里的n和m是10^8

    算了我们简化一下10^6

    肯定裸dp是药丸的

    辣么我们用数学解决

    容斥原理qwq

    总路径-至少经过一个点+至少经过两个点-至少经过三个点+至少经过4个点.........

    我们可以dp出容斥系数

    奇数就减,偶数就加

     复杂度O(t2)

    窝莫得听懂

    来我们看看记忆化搜索

    终于有一道数据范围人类的题了

    如果我们只有一个dfs(x,y)表示从(x,y)走到(n,m)的距离

    我们再dfs中可能多次调用同一个dfs(i,j),这样我们就可以把dfs(i,j)记下来,这就是记忆化搜索

    做题步骤(雾

    似乎搜索可以

    哎参数不多唉

    有的值重复计算了???

    那就记下来

    遇见duliu搜索顺序题怎么办

    记忆化!!!(据说记忆化还有剪枝效果)

    bzoj 3810

    这是个好题

    我们发现合法的一定有一条贯穿整个矩形的线

    那我们就枚举贯穿线,算出左边差异度,右边差异度的最小值

    设dp[i][j][4]为长为i,宽为j的矩形面对大海春暖花开状态

     

    就是把一个矩形切成一堆小矩形

    拓扑图dp

    什么意思?

    就是求从一个入度为0的点到达一个出度为0的点的方案数

    dp[i]=∑dp[j](存在j到i的边)

    我们可以边跑拓扑序,边计算dp[i]

    最后出度为0的点的dp值的和就是答案

     最短路图怎么建?

    如果dis[u]+len[edge[i]]=dis[v],那么edge[i]就在最短路径上

    然后求到T的方案数

    最大子矩阵

    n4乱搞

    其实如果数据保证都是正整数,那就是O(n2)的

    正解:先对行进行组合,选取组合中的行,把每一列上的数加起来,就变成了一行数,然后求最大子段和

     所有组合中最大子段和的最大值就是答案

     举个例子叭

    1 2 -10 6

    2 -1 3 7

    3 -5 9 8

    这个矩阵一共有3行

    那么行的组合有{(1),(2),(3),(1,2),(1,3),(2,3),(1,2,3)}

    先对行1进行合并(其实不用合并),得到1 2 -10 6,跑最大子段和

    行2和行3的省略辣(反正只有一行也没有什么可以合并的)

    接下来才是真正的合并

    取1,2行

    1 2 -10 6

    2 -1 3 7

    同一列上的数字相加:

    第一列:1+2=3

    第二列:2-1=1

    第三列:-10+3=-7

    第四列:6+7=13

    这样得到新的一行数: 3 1 -7 13,跑最大子段和

    然后是对(1,3)(2,3)(1,2,3)这样搞,求出来所有的最大子段和中最大的那个就是答案

    序列上设计dp

    bzoj1003

    f[i]表示前i天的最小中成本

    可以套这个:

    跑最短路,注意此处的最短路要保证在所有天里,起点和终点都可以互达

    f[i]=min{f[j]+jl[j+1][i]*(i-j)+k}

    bzoj1296

    考虑只有一条木板

    f[i][j]:刷到第i个格子,用了j次,最多正确粉刷的格子数

    f[i][j]=max{f[k][j-1]+cnt(j+1,i)}(枚举k)

    cnt:可以搞个前缀和,然后计算[j+1,i]中红色格子的数量,蓝色格子的数量,取max

    再考虑有好多条木板

    g[i][l]表示前i个木板一共刷了l次,最多的数量

    g[i][l]=max{g[i-1][l-x]+fi[m][x]|x≤m}

    什么意思呢?枚举木板i粉刷的次数,找最大值

     

    设左括号为+1,右括号为-1

    则总和为0,任意前缀和≥0

    dp[i][j]前i个位置的前缀和为j的方案数

     我们发现括号序列中的某些部分可以左右括号抵消

    就像这样:(()))) ---------------> ))

    回想一下打怪那个题

    对于回血怪(a[i]-d[i]>0),我们按照d[i]升序排序,对于扣血怪(a[i]-d[i]<0),我们按照a[i]降序排序

    和这个题联系一下

    我们把左括号当做+1,右括号当做-1,先进行化简(左右括号互相抵消)。化简完的序列的右括号一定在左括号的左边,数量用L[i]表示。左括号的数量用R[i]表示,要选出尽可能多的括号序列,也就是要打尽可能多的怪,就按照打怪的方法排序

    f[i][j]表示前i个序列,+1,-1和为j时的最长长度,len[i]是排完序后第i个括号序列的最初长度

    f[i][j]=max(f[i-1][j-L[i]+R[i]]+len[j],f[i-1][j])

    ans=f[n][0]

    有趣的题

    卡特兰数???(不是记搜吗(っ°Д°;)っ)

    对f[n]来说,第一个左括号一定有一个与之匹配的第一个右括号,那就枚举它们中间有多少对括号。若有i对,则贡献是f[i]*f[n-1-i]

    f[n]=∑f[i]*f[n-1-i](0≤i≤n-1)

    上面就是卡特兰数的公式辣

    其他的卡特兰数的问法:

    证明:

    3:左右子树不一样,设左子数的大小为i,右子树的大小就是n-i-1,乘一乘还是原来的公式

    4:这里其实是求的n+2条边的凸多边形

    5:把往右走当做+1,往上走当做-1,保证前缀和为0,最后和为0,还是括号匹配问题

    贪心的选取每个区间的最小值

    这就是那个滑块的题辣

    辣么怎么用单调队列优化这个题呢?

    队头维护最小值,如果最小值的下标小于当前区间的左端点就pop掉,每次插入,如果前面的值比当前插入的值大,就pop掉,这样队头一直是当前区间的最小值辣

    利用辅助数组优化

    .....举个例子

    dp[i][0][0]--------------->dp[i+1][1][1],且第一个串是ab?:

    dp[i+1][1][1]=dp[i][0][0]*f[a][b][?][0][0][1][1]

    说人话:f数组处理16种转移的系数

    为毛是16种?

    例一:合并石子

    ρωρ

    poj3280

    dp[l][r]表示[l,r]变为回文串的最小值

    如果s[i]≠s[j],考虑[i,j-1]和[i+1,j](删掉s[i]或s[j])

    dp[i][j]=min(dp[i+1][j]+add[s[i]],dp[i+1][j]+del[s[i]],dp[i][j-1]+add[s[j]],dp[i][j-1]+del[s[j]])

    若s[i]==s[j],则dp[i][j]=dp[i+1][j-1]

    括号最大匹配

    dp[i][j]表示[i,j]内最长的合法子序列

     考虑当前的括号是否匹配

    没匹配:dp[i][j]=dp[i+1][j]

    匹配:枚举和谁匹配

             若i和k匹配,则dp[i+1][k-1]是合法的,dp[k+1][j]还要选出一个合法的子序列

             所以dp[i][j]=max{dp[i+1][k-1]+dp[k+1][j]}

    为毛上一个题不枚举和谁匹配呢?明明好像都是回文串的亚子

    因为上一个题只能是回文串,但这个题可以是多个回文串拼起来

    n≤100:枚举断点

    n显然超过O(n3)允许的数据:一般来说是只考虑边界情况,不枚举断点           

    bzoj1900

    dp[i][j]:折叠区间[i,j]的最小代价

    我们可以枚举断点唉

    一个折叠的区间可能由两个区间拼接而成,就枚举断点

    ①:自身可以压缩:找循环节的长度k(hash),dp[i][j]=dp[l][k]+2+区间长度/(k-l)+1

    ②:由可以压缩的子区间拼接而成,dp[i][j]=min{dp[i][k]+dp[k+1][j]}

    环形问题

    就是把一个环,断开成链,然后再把原来的链复制一遍接到后面

    能量项链

    如果它是个链:dp[i][j]=max{dp[i][k]+dp[k+1][j]+h[i]*h[k]*t[j]}

    但是环可以搞出dp[4][3]这种操作,所以....

    要环变链

     

    dp[i][j]=min{dp[i][k]+dp[k+1][j]+gcd(a[i],a[j])}

    似乎好像真的没什么东西

                                                                                        

  • 相关阅读:
    使用Orachard与Bootstrap建站心得
    【02C语言】11函数的声明和定义
    【02C语言】09流程控制
    有趣的linux命令
    杭州哪家整容医院比较有威望?
    DDD:在基于关系数据库的领域,聚合的边界等于并发管理的边界。
    TOGAF架构开发方法(ADM)之业务架构阶段
    构建一个真实的应用电子商务SportsStore(八)
    Lucene分词组件盘古与mmseg4j评测
    .NET PDB文件到底是什么?
  • 原文地址:https://www.cnblogs.com/lcez56jsy/p/11310126.html
Copyright © 2011-2022 走看看