有n堆石子排成一圈,每次选择相邻的两堆石子,将其合并为一堆,记录该次合并的得分为两堆石子个数之和。已知每堆石子的石子个数,求当所有石子合并为一堆时,最小的总得分。
Input
第一行一个整数n(1 <= n <= 200),表示石子堆数; 第二行n个整数a(1 <= a <= 100),表示每堆石子的个数,这些石子首尾相接排成一圈。
Output
一个整数,表示最小总得分。
Sample Input
5
7 6 5 7 100
Sample Output
175
它其实是这道题(石子合并1)的变形。
我们只要改变石子合并1的状态转移方程即可。
概率环形问题的区间问题,存在这样的情况,假设石子堆为4个,即我们的最优区间可能是3,4,1,2。
这样的情况是线性石子堆无法解决的,因为线性石子堆我们只需考虑1,2,3,4的区间最优即可。所以我们的外循环就不是长度了,而变为开始区间的索引,内循环则为从这个区间开始往后数几个,然后k则为划分这个区间。所以状态转移方程为:
static int getSum(int index, int length){
int sum = 0;
for(int i = 1; i <= length; i++){
sum += nums[index];
index++;
if(index > n){
index = 1;
}
}
return sum;
}
package com.special.SDNUOJ;
import java.util.Arrays;
import java.util.Scanner;
/**
* Created by Special on 2018/6/4 11:30
*/
public class SDNUOJ1048 {
static int[] nums;
static int n;
static int[][] dp;
static int getSum(int index, int length){
int sum = 0;
for(int i = 1; i <= length; i++){
sum += nums[index];
index++;
if(index > n){
index = 1;
}
}
return sum;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while(input.hasNext()){
n = input.nextInt();
nums = new int[n + 1];
dp = new int[n + 1][n + 1];
for(int i = 1; i <= n; i++) {
nums[i] = input.nextInt();
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
for(int j = 1; j <= n; j++){
for(int i = 1; i <= n; i++){
int sum = getSum(i, j);
dp[i][1] = 0;
for(int k = 1; k < j; k++){
dp[i][j] = Math.min(dp[i][j],
dp[i][k] + dp[(i + k - 1) % n + 1][j - k] + sum);
}
}
}
int ans = Integer.MAX_VALUE;
for(int i = 1; i <= n; i++){
ans = Math.min(ans, dp[i][n]);
}
System.out.println(ans);
}
}
}
既然环形的区间可能从任意位置开始,所以我们可以化环形为直线型问题,问题转为石子合并1问题,转换方法为把当前的石子堆复制一份放到后面,这样就可以了。
为了减少不必要的计算问题,比如n = 4. 我们再求dp[5][6]的最优值,其实它与dp[1][2]是等价,故而我们只需求dp[4][…]的最优区间即可,dp[5+]之后就不用考虑,但是这样在求dp[3][6]最优区间的值时,遇到划分dp[3][4] 和dp[5][6],这时我们就要把dp[5][6]转化为dp[1][2]。因为上面我们只算到打破dp[4]开头的最优区间,后面的就没算。
package com.special.SDNUOJ;
import java.util.Arrays;
import java.util.Scanner;
/**
* Created by Special on 2018/6/4 11:51
*/
public class SDNUOJ1048_02 {
static int[] nums, sum;
static int n;
static int[][] dp;
static int getSum(int i, int j){
int result = 0;
if(j > n){
result = sum[j % n] + sum[n] - sum[i - 1];
}else {
result = sum[j] - sum[i - 1];
}
return result;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while(input.hasNext()){
n = input.nextInt();
dp = new int[2 * n + 1][2 * n + 1];
nums = new int[2 * n + 1];
sum = new int[n + 1];
for(int i = 1; i <= n; i++){
nums[i] = input.nextInt();
nums[n + i] = nums[i];
sum[i] = sum[i - 1] + nums[i];
}
for(int i = 1; i <= 2 * n; i++){
Arrays.fill(dp[i], Integer.MAX_VALUE);
dp[i][i] = 0;
}
for(int len = 1; len < n; len++){
for(int i = 1; i <= n; i++){
int j = i + len;
int sum = getSum(i, j);
for(int k = i; k < j; k++){
dp[i][j] = Math.min(dp[i][j], dp[i][k] +
(k >= n ? dp[(k + 1) % n][j % n] : dp[k + 1][j]) + sum);
}
}
}
int result = Integer.MAX_VALUE;
for(int i = 1; i <= n; i++){
result = Math.min(result, dp[i][i + n - 1]);
}
System.out.println(result);
}
}
}