目录
282. 石子合并
题目:
- 每堆石子有一定的质量,可以用一个整数来描述,现在要将这 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
题目:
题目太长不想解释,点开链接自己看呗~
思路:
- 环形问题,将数组开成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);
}
}