算法实现题 3-10 最小m段和问题
问题描述:
给定 n 个整数组成的序列,现在要求将序列分割为 m 段,每段子序列中的数在原序列中
连续排列。如何分割才能使这 m 段子序列的和的最大值达到最小?
编程任务:
给定 n 个整数组成的序列,编程计算该序列的最优 m 段分割,使 m 段子序列的和的最大值达到最小。
数据输入:
由文件 input.txt 提供输入数据。文件的第 1 行中有 2 个正整数 n 和 m。正整数 n 是序列的长度;正整数 m 是分割的断数。接下来的一行中有 n 个整数。
结果输出:
程序运行结束时,将计算结果输出到文件 output.txt 中。文件的第 1 行中的数是计算出的 m 段子序列的和的最大值的最小值。
输入文件示例1
1 1
10
输出文件示例1
10
输入文件示例2
5 3
1 2 3 4 5
输出文件示例 2
6
参考以下文章:
https://blog.csdn.net/qq_38367681/article/details/81252798
https://blog.csdn.net/dms2017/article/details/89607379
https://www.cnblogs.com/ljy-endl/p/11610549.html 界面好看
用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<bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int a[1005];
int main() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
for(int i=1; i<=n; i++)
dp[i][1]=dp[i-1][1]+a[i];
//dp[i][1]表示分成1段时,最大值
//用dp[i][j]存储长度为i,分j段后其子序列和的最大值的最小值
for(int i=1; i<=n; i++) {//枚举长度
for(int j=2; j<=m; j++) {//枚举分段数
int min=9999;
for(int k=1; k<=i; k++) {//最后一段的每一次的分组情况
int t=max(dp[k][j-1],dp[i][1]-dp[k][1]);
if(t<min) //寻找最小值并赋值
min=t;
}
dp[i][j]=min;
}
}
cout<<dp[n][m]<<endl;
return 0;
}
/*
5 3
1 2 3 4 5
6
*/
#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int sum[1005][1005];
int a[1005];
int main() {
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
dp[i][j]=9999;//dp都要预先默认一个较大的值
for(int i=1; i<=n; i++) {
int num=0;
for(int j=i; j<=n; j++) {
num=num+a[j];
sum[i][j]=num;//sum数组存储第i到第j之间j-i+1个数字之和
}
dp[i][1]=sum[1][i];
}
for(int i=2; i<=n; i++) {
for(int j=2; j<=i&&j<=m; j++) {
for(int k=1; k<i; k++) {
dp[i][j]=min(dp[i][j],max(dp[k][j-1],sum[k+1][i]));
}
}
}
cout<<dp[n][m]<<endl;
return 0;
}