【蓝桥杯集训27】区间DP(2 / 2)

目录

282. 石子合并

区间dp常用模板

320. 能量项链 - 区间合并dp


282. 石子合并

活动 - AcWing 

题目:

  • 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆
  • 每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同
  • 例如有 4 堆石子分别为 1 3 5 2, 我们可以先合并 1、2 堆,代价为 4,得到 4 5 2, 又合并 1、2 堆,代价为 9,得到 9 2 ,再合并得到 11,总代价为 4+9+11=24
  • 如果第二步是先合并 2、3 堆,则代价为 7,得到 4 7,最后一次合并代价为 11,总代价为 4+7+11=22
  • 找出一种合理的方法,使总的代价最小,输出最小代价

思路:

定义f[i][j]为将【i,j】这一段石子合并成一堆的方案的最小代价

i是左端点,j是右端点

  • 当i<j时,f[i][j]=min(f[i][k]+f[k+1][j]+s[j]-s[i-1]) 这两堆石子各自的最小代价+合成这两堆的代价
  • 当i==j时,只有一段石子,则代价为0

区间dp常用模板

所有的区间dp问题枚举时,第一维通常是枚举区间长度,len=1时,只有1堆,代价为0

因此枚举从 len = 2 开始;第二维枚举左端点 i (右端点 j 自动获得,j = i + len - 1) 

for(int len=1;len<=n;len++) //枚举长度
{
    for(int i=1;i+len-1<=n;i++) //枚举左端点
    {
        int j=i+len-1; //得到右端点
        if(len==1) 
        {
            f[i][j]=0;
            continue;
        }
                
        for(int k=i;k<j;k++) //枚举分割线k
            f[i][j]=Math.min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
    }
}
import java.util.*;

class Main
{
    static int N=330;
    static int[][] f=new int[N][N];
    static int[] s=new int[N];
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        for(int i=1;i<=n;i++)
        {
            s[i]=sc.nextInt();
            s[i]+=s[i-1];
        }
        
        for(int i=1;i<=n;i++)  Arrays.fill(f[i],0x3f3f3f3f);
        
        for(int len=1;len<=n;len++)
        {
            for(int i=1;i+len-1<=n;i++)
            {
                int j=i+len-1;
                if(len==1) 
                {
                    f[i][j]=0;
                    continue;
                }
                
                for(int k=i;k<j;k++) //枚举分割线k
                    f[i][j]=Math.min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
            }
        }
        System.out.print(f[1][n]);
    }
}

 

320. 能量项链 - 区间合并dp

活动 - AcWing

题目:

题目太长不想解释,点开链接自己看呗~

思路:

  • 环形问题,将数组开成2*n
  • 定义f[i][j]为【头标记i,尾标记j】这段能量珠合成的最大能量
  • i是头标记,j=i+len-1是尾标记,由于头尾标记中至少有一个中间值,因此len从3开始枚举
  • 枚举中间值k,求出所有情况的能量
  • 最后枚举1~n的能量球为头标记,看哪个球为起点能量值最大
import java.util.*;

class Main
{
    static int N=210;
    static long[][] f=new long[N][N]; //f[i][j]:把头标记i 尾标记j这段能量珠合成一段的最大能量
    static int[] w=new int[N]; //为能量珠的能量
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        for(int i=1;i<=n;i++)
        {
            w[i]=sc.nextInt();
            w[n+i]=w[i]; //解决环形问题
        }
        
        for(int len=3;len<=2*n;len++) //i和j间至少有一个中间值才能合成
        {
            for(int i=1;i+len-1<=n*2;i++)
            {
                int j=i+len-1;
                //k为合并两珠子的中间值(前一个的尾,后一个的头) 
                for(int k=i+1;k<j;k++) //[i,i+1][i+1,j]————[i,j-1][j-1,j]
                    f[i][j]=Math.max(f[i][j],f[i][k]+f[k][j]+w[i]*w[k]*w[j]);
            }
        }
        long res=0; //以每一个珠子为起点枚举,看以哪个为起点能量值最大
        for(int i=1;i<=n;i++) res=Math.max(res,f[i][i+n]);
        System.out.print(res);
        
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值