问题描述:给定n个整数组成的序列,现在要求将序列分割为m段,每段子序列中的数在原序列中连续排列。如何分割才能使这m段子序列的和的最大值达到最小?
问题说明:给定序列:5 4 3 2 1 n=5, m=2 可分为 5 | 4 | 3 2 1 其子序列和的最大值的最小值为1+2+3=6。
问题解析:用dp[i][j]存储长度为i,分j段后其子序列和的最大值的最小值,那么它由两部分构成:
当j=1时,dp[i][1]表示的是长为i的整个序列的和;
当j>1时,dp[i][j] = MIN(for(k=1; k<=i; k++)MAX(dp[k][j-1], dp[i][1] - dp[k][1]));这当中k表示的是分段的最后一段子序列的开始下标,所以dp[k][j-1]是前面j-1段子序列和的最大值的最小值,dp[i][1] - dp[k][1]是最后一段子序列的和。所以取这两段中的最大值,在k值变化过程中取得到的最小值就OK了。
代码:
#include <stdio.h>
int dp[100][100] ; //dp[i][j]存储0~i的j个分组的和的最大值的最小值
int MAX(int n1, int n2){
if(n1 < n2)
return n2 ;
else
return n1 ;
}
void calculate(int a[], int n, int m){
int i, j, k, temp, min ;
if(n == 0){
printf("数组为空!\n") ;
return ;
}
for(i=1; i<=n; i++){ //当j=1时表示整个序列的和
dp[i][1] = dp[i-1][1] + a[i] ;
}
for(i=1; i<=n; i++){
for(j=2; j<=m; j++){
min = 99999 ;
for(k=1; k<=i; k++){ //最后一段的每一次的分组情况
temp = MAX(dp[k][j-1], dp[i][1] - dp[k][1]) ;
if(temp < min){ //寻找最小值并赋值
min = temp ;
}
}
dp[i][j] = min ;
}
}
printf("%d", dp[n][m]) ;
}
int main()
{
int a[100] ;
int i, n, m ;
printf("请输入数组长度:") ;
scanf("%d", &n) ;
printf("请输入分段个数:") ;
scanf("%d", &m) ;
printf("请输入数组内容:") ;
for(i=1; i<=n; i++){
scanf("%d", &a[i]) ;
}
calculate(a, n, m) ;
printf("\n") ;
return 0 ;
}