石子合并
描述:
在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。
输入格式:
数据的第 1 行是正整数 N ,表示有 N 堆石子。
第 2 行有 N 个整数,第 i 个整数 a**i 表示第 i 堆石子的个数。
输出格式:
输出共 2 行,第 1 行为最小得分,第 2 行为最大得分。
输入样例:
4
4 5 9 4
输出样例:
43
54
dmin
i/j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 9 | 27 | 44 | ||||
2 | 0 | 14 | 31 | 44 | ||||
3 | 0 | 13 | 25 | 43 | ||||
4 | 0 | 8 | 21 | 43 | ||||
5 | 0 | 9 | 27 | 44 | ||||
6 | 0 | 14 | 31 | |||||
7 | 0 | 13 | ||||||
8 | 0 |
dmax
i/j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 9 | 32 | 54 | ||||
2 | 0 | 14 | 32 | 54 | ||||
3 | 0 | 13 | 30 | 52 | ||||
4 | 0 | 8 | 22 | 54 | ||||
5 | 0 | 9 | 32 | 54 | ||||
6 | 0 | 14 | 32 | |||||
7 | 0 | 13 | ||||||
8 | 0 |
直线石子问题:
import java.util.Scanner;
@SuppressWarnings("all")
public class Stone_merging_problem_01 {//石子合并问题,和背包,回文串问题一样的解题思路石子直线的情况
public static void main(String args[]){
int n;
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
int []dp = new int[n + 1];
int [][]dpmin = new int[n + 1][n + 1];//存储石子合并最小
int [][]dpmax = new int[n + 1][n + 1];//存储石子合并最大
int []sum = new int[n + 1];//存储前n项和
sum[0] = 0;//初始化
dp[0] = 0;//其实这项没啥用,但初始化一下
for(int i = 1;i <= n;i++){//存储前n项和,后面更好计算
dp[i] = scanner.nextInt();
sum[i] = sum[i - 1] + dp[i];
}
for(int l = 2;l <= n;l++){//子问题开始规模,长度
for(int i = 1;i <= n - l + 1;i++){//起始位置
int j = i + l - 1;//终止位置
dpmin[i][j] = 200000;//假设石子的总数上限为200000;保证赋第一个值
dpmax[i][j] = -1;//保证赋第一个值
int temp = sum[j] - sum[i - 1];//从i - 1 到 j 的和
for(int k = i;k < j;k++){//动态规划
dpmin[i][j] = Math.min(dpmin[i][j],dpmin[i][k] + dpmin[k + 1][j] + temp);
dpmax[i][j] = Math.max(dpmax[i][j],dpmax[i][k] + dpmax[k + 1][j] + temp);
}
}
}
System.out.println(dpmin[1][n]);
System.out.println(dpmax[1][n]);
}
}
环形回路石子问题
import java.util.Scanner;
@SuppressWarnings("all")
public class Stone_merging_problem_02 {
public static void main(String args[]){//石子合并问题 动态规划 环形回路
int n,Min = 200000,Max = -1;
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
int []dp = new int[2*n + 1];//环形石子要考虑n种情况
int [][]dpmin = new int[2*n + 1][2*n + 1];
int [][]dpmax = new int[2*n + 1][2*n + 1];
for(int i = 1;i <= n;i++){//存储前n项和,后面更好计算
dp[i] = scanner.nextInt();
dp[i + n] = dp[i];
}
for(int l = 2;l <= n;l++){//子问题开始规模,长度
for(int i = 1;i <= 2*n - l + 1;i++){//起始位置 i + l - 1 <= 2 * n
int j = i + l - 1;//终止位置
dpmin[i][j] = 200000;//假设石子的总数上限为200000;保证赋第一个值
dpmax[i][j] = -1;//保证赋第一个值
int temp = dp[0];
for(int m = i;m <= j;m++){
temp += dp[m];
}
for(int k = i;k < j;k++){//动态规划
dpmin[i][j] = Math.min(dpmin[i][j],dpmin[i][k] + dpmin[k + 1][j] + temp);
dpmax[i][j] = Math.max(dpmax[i][j],dpmax[i][k] + dpmax[k + 1][j] + temp);
}
}
}
for(int i = 1;i <= n;i++){
Min = (dpmin[i][n + i -1] < Min)?dpmin[i][n + i - 1]:Min;
Max = (dpmax[i][n + i -1] > Max)?dpmax[i][n + i - 1]:Max;
}
System.out.println(Min);
System.out.print(Max);
}
}