最小m段和问题
1.问题描述:
问题描述:给定n个整数组成的序列,现在要求将序列分割为m段,每段子序列中的数在原序列中连续排列。如何分割才能使这m段子序列的和的最大值达到最小?
算法设计:给定n个整数组成的序列,计算该序列的最优m段分割,使m段子序列的和的最大值达到最小。
数据输入:由文件input.txt提供输入数据。文件的第1行中有2个正整数n和m。正整数n是序列的长度;正整数m是分割的段数。接下来的一行中有n个整数。
结果输出:将计算结果输出到文件output.txt。 文件的第1行中的数是计算出的m段子序列的和的最大值的最小值。
2.最优子问题:
把前 i 个数据划分成 j 段(i>=j),最优划分中的前 j-1 段的最后一个数据是k号(k>=j-1),必然有:这前 j-1段就是前 k 个数据的最优 j-1划分。
3.状态转移方程:
t [i] [j] 表示前 i 个数据划分成 j 段的子段和中的最大值,且这个最大值是所有划分方案中最小的。
t [i] [j] = min(j<=k<=i) { max(t [k] [j-1] , ∑(k<=x<=i)ax ) }
4.实现:
M[i][j]记录前i个数据划分成j段的最优划分方法中的最后一段的左端点。
时间复杂度:O(mn^2)
int solve(int a[],int n,int m){
int i,j,k,t[n+1][m+1],M[n+1][m+1],sum,getMax;
//初始化
for(i=0;i<=n;i++)//其他
for(j=0;j<=m;j++)
t[i][j]=M[i][j]=MaxInt;
t[0][0]=M[0][0]=0;//0段&&0数据
for(j=1;j<=m;j++){//划分成1~m段
for(i=j;i<=n;i++){//用j~n个数据
sum=0;//i个数据划分成j段。第j段的和
for(k=i;k>=j;k--){//第j段的左端点
sum+=a[k-1];
getMax=max(t[k-1][j-1],sum);
if(getMax<t[i][j] ){
t[i][j]=getMax;
M[i][j]=k;//记录划分点
}
}
}
}
//输出
i=n;j=m,k=m-1;
int mark[m+1];
while(i>=1&&j>=1){
mark[k]=M[i][j];
i=mark[k]-1;
k--;j--;
}
mark[m]=n+1;
i=1;k=0;
while(i<=n){
cout<<"{";
for(;i<mark[k+1];i++){
cout<<a[i-1]<<' ';
}
cout<<"}";
k++;
}
cout<<"\n";
return t[n][m];
}