• 区间dp


    区间dp就是在一段区间上的动态规划

    对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值

    石子合并问题

    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆
    每次只能将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值

    先根据dp的思想划分成子问题,先求出每两个合并的最小代价,然后每三个的最小代价,依次知道n个

    定义状态 dp(i,j) = 从第i个石子到第j个石子的合并最小代价

    dp(i,j) = min(dp(i,k) + dp(k+1,j))
    

    那就可以从小到大依次枚举让石子合并,直到所有的石子都合并

    核心代码:

    for(l = 2; l <= n; ++l)//枚举区间长度
    {
        for(i = 1; i <= n - l + 1; ++i)//枚举区间左端点
        {
            j = i + l - 1;//根据左端点和区间长度求区间右端点
            if(j > n)
                break;
            dp[i][j] = 0x3f3f3f3f;
            for(k = i; k < j; ++k)
            {
                dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j] + sum[j] - sum[i-1]);
            }
        }
    }
    

    Dire Wolf

    你现在面对一群狼,每只狼都有一定的主动攻击力和附带攻击力,你每杀死一只狼,你会受到这只狼的 主动攻击力+旁边两只狼的附带攻击力 的伤害

    现在问你如何选择杀狼的顺序使的杀完所有狼时,自己受到的伤害最小

    Hint:狼杀死后直接消失,左右两只狼会变成相邻,不考虑成环
    设dp(i,j)为消灭编号从i到j只狼的代价,那么结果就是dp(1,n)

    枚举k作为最后一只被杀死的狼,此时会受到a[k]和b[i-1] b[j+1]的伤害

    dp(i,j)=min(dp(i,j), dp(i,k-1)+dp(k+1,j,j)+a[k]+b[i-1]+b[j+1])
    
    dp(i,j)=a[i]+b[i-1]+b[j+1];
    
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 205;
    int a[maxn],b[maxn],dp[maxn][maxn],n;
    
    int main()
    {
        int T;
        scanf("%d",&T);
        int kase = 0;
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1; i<=n; ++i)
                scanf("%d",a+i);
            for(int i=1; i<=n; ++i)
                scanf("%d",b+i);
    
            memset(dp,0x3f,sizeof dp);
            for(int i=0; i<=n+1; ++i)
                dp[i][i+1] = 0;
            for(int l=2; l<=n+1; ++l)
            {
                for(int i=0; i<=n+1-l; ++i)
                {
                    int j = i + l;
                    for(int k=i+1; k<j; ++k)
                    {
                        dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]+a[k]+b[i]+b[j]);
                    }
                }
            }
            printf("Case #%d: %d
    ",++kase,dp[0][n+1]);
        }
        return 0;
    }
    

    Halloween Costumes

    有N个宴会,对于每一个宴会,女主角都要穿一种礼服,礼服可以套着穿,但是脱了的不能再用,参加宴会必须按顺序来,从第一个到第N个,每次只能穿1件或者脱下多件,问参加这些宴会最少需要几件礼服

    分析:首先我们使用dp(a,b)来表示区间 a~b 的答案,那么对于第 i 件衣服,我们有

    1. 如果在之后的区间内都不再重复利用这件衣服,那么明显 dp(i,j) = dp(i+1,j) + 1;

    2. 如果在之后的区间 i+1 ~ j 中存在一件衣服 k 是跟 i 一样的,那么我们便可以考虑是不是可以将i那件衣服在k这个地方重复利用,
      那么转移方程为 dp(i,j) = min(dp(i,j) , dp(i,k-1)+dp(k+1,j)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 105;
    const int inf = 0x3f3f3f3f;
    int n, a[maxn];
    int dp[maxn][maxn];
    
    int main()
    {
        int T;
        scanf("%d", &T);
        int kase = 0;
        while(T--)
        {
            scanf("%d", &n);
            for(int i = 1; i <= n; ++i)
            {
                scanf("%d", &a[i]);
            }
            for(int i = 1; i <= n; ++i)
            {
                dp[i][i] = 1;
            }
            for(int len = 2; len <= n; ++len)
            {
                for(int i = 1; i + len - 1 <= n; ++i)
                {
                    int j = i + len - 1;
                    dp[i][j] = dp[i][j - 1] + 1;
                    for(int k = i; k < j; ++k)
                    {
                        if(a[k] == a[j])
                        {
                            if(k == j - 1)
                                dp[i][j] = min(dp[i][j], dp[i][k]);
                            else
                                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]);
                        }
                    }
                }
            }
            printf("Case %d: %d
    ", ++kase, dp[1][n]);
        }
        return 0;
    }
    

    Polygon(环形dp)

    多边形游戏,有N个顶点的多边形(3≤N≤50) ,多边形有N条边,每个顶点中有一个数字(可正可负),每条边上或者是“+”号,或者是“*”号,边从1到N编号,首先选择一条边移去,然后进行如下操作:

    1. 选择一条边E和边E连接着的两个顶点V1,V2
    2. 用一个新的顶点代替边E和V1、V2,新顶点的值为V1、V2中的值进行边上代表的操作得来(相加或相乘)

    当最后只剩一个顶点,没有边时,游戏结束。现在的任务是编程求出最后的顶点能获得的最大值,以及输出取该最大值时,第一步需移去的边,如果有多条符合条件的边,按编号从小到大输出

    先将环拆成链,再去枚举若拆掉第i条边所得到的最大值

    显然,不仅仅要去求i,j区间里的最大值,还要求最小值,因为最小值有可能负数*负数得到一个很大的正数

    构造状态,用 dp(i,j) 来代表以顶点i开头,长度(即包含的顶点数)为j时的最优值。

    当选定开头顶点 i 后,就默认 i 前边的那条边被删除了(因为用不到它了);

    但是此题有可能顶点是负数,那就存在负负相乘得正的情况,需同时维护最大值和最小值。

    dp(i,j,0) = 以i开头,长度j时的最小值

    dp(i,j,1) = 以i开头,长度j时的最大值

    An old Stone Game(平行四边形优化)

  • 相关阅读:
    Spring boot --- Spring Oauth(三)
    Spring boot --- Spring Oauth(一)
    JavaScript 学习(一)
    Springboot --- Spring Security (一)
    HTML 学习笔记 JavaScript (节点)
    HTML 学习笔记 JavaScript(事件)
    HTML 学习笔记 JavaScript (DOM)
    HTML 学习笔记 CSS3 (多列)
    HTML 学习笔记 CSS3(Animation)
    HTML 学习笔记 CSS3(过度 transition)
  • 原文地址:https://www.cnblogs.com/zhxmdefj/p/11172222.html
Copyright © 2020-2023  润新知