最小M段和问题:
- 问题描述:
给定n个整数组成的序列,现在要求将序列分割成m段,每段子序列中的数在原数列中连续排列。如何分割才能使m段子序列的和的最大值达到最小?
- 数据输入:
第一行中有2个正整数n和m。正整数n是序列的长度,正整数m是分割的段数。接下来的一行中有n个整数。
- 算法思想:
使用dp[i][j]放置将前i个数分成j段的最大最小值。
所以dp[i][1]就是前i个数的和,dp[i][1] = dp[i-1][1] + a[i];
当j>1的时候,假设前k个数为j-1段,从k~i为第j段,所以前j-1段的最大最小值为:dp[k][j-1] (前k个数分为j-1段).
最后一段为:dp[i][1]-dp[k][1] (前i个数的和减去前k个数的和)
这两个值中选取一个最大值,当所有情况讨论结束后,选出结果中最小的作为dp[i][j]的值。
- 递推方程:
dp[i][j] = min(max{dp[i][1]- dp[k][1],dp[k][j-1]})
代码:
package DongTaiGuiHua;
/*
* 递推方程:
* dp[i][j] = min(max{dp[i][1]- dp[k][1],dp[k][j-1]})
* */
import java.util.Scanner;
public class 最小m段和问题 {
public static int a[] = new int[100]; // 存放输入的n位整数
public static int dp[][] = new int[1024][1024]; // dp中存放长度从1到n的所有划分结果的最大最小值,最后一个求得的dp[i][j]放置将i个数分成j段的最大最小值。。
public static void main(String[] args) {
int n, m, maxvalue = 0;
Scanner input = new Scanner(System.in);
n = input.nextInt();// 输入整数位数n
m = input.nextInt();// 输入划分个数m
for(int i = 1; i <= n; i++) {
a[i] = input.nextInt();// 输入n位等待划分的正整数
}
for(int i = 1; i <= n; i++) {
dp[i][1] = dp[i-1][1] + a[i];// dp[i][1]就是前i个数的和
}
for(int i = 1; i <= n; i++) {
for(int j = 2; j <= m; j++){
// 把i位整数划分成j段
int minvalue = 99999; // 保存最小值用,要保存最小值就需要初始化成一个极大值
for (int k = j - 1; k < i; k++) {
// K是划分位置,只用k划分成两段,因为划分得到的前半部分都是讨论过的。
int temp = Max(dp[i][1] - dp[k][1], dp[k][j-1]);
// 前j-1段的最大最小值为:dp[k][j-1];(前k个数划分成j-1段)
// 最后一段为:dp[i][1] - dp[k][1] ;(前i个数的和减去前K个数的和)
if(temp < minvalue) {
minvalue = temp; // 保存最小值
}
}
dp[i][j] = minvalue; // 把所有k情况下的最小值赋值给dp[i][j]。
}
}
System.out.println(dp[n][m]);
}
public static int Max(int a, int b) {
if (a > b) {
return a;
}else {
return b;
}
}
}