3-6 石子合并问题(圆排列)
问题描述
在一个圆形操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。
规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
分析
圆排列的石子合并问题可以转化为n个直线排列的石子合并问题
圆排列:A1,A2,...,AnA1,A2,...,An
设计扩充的直线排列:A1,A2,...,An,A1,A2,...,An−1A1,A2,...,An,A1,A2,...,An−1
上面设计的扩充直线排列,可以表示圆排列的n种情形。
设扩充的n-1个元素为An+iAn+i(1<=i<=n-1),则An+iAn+i=AiAi(1<=i<=n-1)。这样就将n堆石子的圆排列合并问题转化为一个2n-1堆石子的直线排列合并问题。
设m[i, j], 1<=i<=j<=2n-1 为合并Ai...jAi...j的最优值,则所述圆排列中n种情形的最优值分别为:m[1, n], m[2, n+1], … , m[n, 2n-1]。
min(或max){m[i, n+i-1]} (1<=i<=n)即为所求圆排列石子合并问题的最优值。
Java
import java.util.Arrays;
import java.util.Scanner;
public class ShiZiHebing {
private static int n;
private static int[][] m;
private static int[][] s;
private static int[] a;
private static int[] backup;
private static int minScore;
private static int maxScore;
public static void main(String[] args){
Scanner input = new Scanner(System.in);
while (true){
n = input.nextInt();
a = new int[2*n];
m = new int[2*n][2*n];
s = new int[2*n][2*n];
for(int i=1; i<=n; i++)
a[i] = input.nextInt();
//a会改变,备份一下
backup = Arrays.copyOf(a, 2*n);
minScore = circleMerge(a,0);//type为0,最小得分
//还原
m = new int[2*n][2*n];
// s = new int[2*n][2*n];
maxScore = circleMerge(backup, 1);//type为1,最大得分
System.out.println(minScore);
System.out.println(maxScore);
}
}
//type=0: 最小得分; type=1: 最大得分
private static int circleMerge(int[] a, int type){
for(int i=1; i<=n-1; i++)
a[n+i] = a[i];
n = 2*n-1;
if(type == 0)
minSum(a);
else
maxSum(a);
n = (n+1)/2;
int mm = m[1][n];
for(int i=2; i<=n; i++)//圆排列 转化为 直线排列后,要遍历一下
if((type==0) && (m[i][n+i-1]<mm) || (type==1) && (m[i][n+i-1]>mm))
mm = m[i][n+i-1];
return mm;
}
private static void minSum(int[] a){
for(int i=2; i<=n; i++)
a[i] = a[i] + a[i-1];
for(int r=2; r<=n; r++)
for(int i=1; i<=n-r+1; i++){
int j = i+r-1;
int i1 = i+1;
int j1 = j;
if(s[i][j-1] > i)
i1 = s[i][j-1];
if(s[i+1][j] > i)
j1 = s[i+1][j];
m[i][j] = m[i][i1-1] + m[i1][j];
s[i][j] = i1;
for(int k=j1; k>=i1+1; k--){
int q = m[i][k-1] + m[k][j];
if(q < m[i][j]){
m[i][j] = q;
s[i][j] = k;//记录合并的断开位置k
}
}
m[i][j] = m[i][j] + a[j] -a[i-1];
}
}
private static void maxSum(int[] a){
for(int i=2; i<=n; i++)
a[i] = a[i] + a[i-1];
for(int r=2; r<=n; r++)
for(int i=1; i<=n-r+1; i++){
int j = i+r-1;
if(m[i+1][j] > m[i][j-1])
m[i][j] = m[i+1][j] + a[j] - a[i-1];
else
m[i][j] = m[i][j-1] + a[j] - a[i-1];
}
}
}
Input & Output
5
2 3 3 5 2
34
47
4
4 4 5 9
43
54
Reference
王晓东《计算机算法设计与分析》(第3版)P90

本文探讨了石子合并问题在圆排列情况下的解决方案,通过转化成直线排列问题,利用动态规划计算合并石子的最小和最大得分。通过分析2n-1堆石子的直线排列,找到圆排列的最优值。提供了问题描述、算法分析及Java实现的相关内容。
1729

被折叠的 条评论
为什么被折叠?



