问题
摆放N堆石子,现在要将石子有次序地合并成一堆
规定每次只能选相邻2堆石子合并成新的一堆,
并将新的一堆石子数记为该次合并得分(每次合并代价都要记录,求的是代价和最小)
求出将n堆石子合并成一堆的最小得分(或者最大得分)合并方案
示例
第一行输入 5
第二行输入 2 3 5 7 10
输出 31
思想
设置一张dp表dp[L][R]L>R位置不需要管 对角线位置为0,需要初始化填倒数第二条对角线的值,这里通过计算i位置相邻的i+1位置的石子数差值进行初始化同时需要一张二维索引表记录对应合并位置的最佳位置。 dp[0][n]位置是我们的答案。
代码
public static int m(int[]s, int l,int r) {
return s[r]- s[l];
}
public static int[] sum(int[] arr) {
int n = arr.length;
int[] s = new int[n+1];
s[0] = 0;
for(int i= 0;i<n;i++) {
s[i+1] = s[i] + arr[i];
}
return s;
}
public static int minscore2(int[] stone) {
if(stone == null||stone.length<2) {
return 0;
}
int n = stone.length;
int[][] dp = new int[n][n];
int[][] index = new int[n][n];
int[] s = sum(stone);
for(int i = 0;i<n-1;i++) {
index[i][i+1] = i;
dp[i][i+1] = m(s, i, i+1);
}
for(int l =n-3;l>=0;l--) {
for(int r = l+2;r<n;r++) {
int choose = -1;
int next = Integer.MAX_VALUE;
for(int leftend = index[l][r-1];leftend<=index[l+1][r];leftend++) {
int cur = dp[l][leftend]+dp[leftend+1][r];
if(cur<next) {
next = cur;
choose = leftend;
}
}
index[l][r] = choose;
dp[l][r] = next + m(s, l, r);
}
}
return dp[0][n-1];
}
public static int[] randomArray(int len, int maxValue) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * maxValue);
}
return arr;
}
public static void main(String[] args) {
Scanner scanner =new Scanner(System.in);
int L = Integer.parseInt(scanner.nextLine());
int[] stone = new int[L];
for(int i=0;i<L;i++) {
stone[i] = scanner.nextInt();
}
int ans = minscore2(stone);
System.out.println(ans);
}