最大值最小化

最大值最小化

maxmin.pas/cpp

题目描述:

把一个包含n 个正整数的序列划分为m 个连续的子序列(每个正整数恰

好属于一个序列)。设第i 个序列的各数之和为S(i),你的任务是让所

有S(i)的最大值尽量小。例如序列1 2 3 2 5 4 划分成3 个序列的最优

方案为1 2 3|2 5 |4,其中S(1)、S(2)、S(3)分别为6、7、4,最大值

为7;如果划分成1 2|3 2|5 4,则最大值为9,不如刚才的好。

n<=10^6,所有数之和不超过10^9。

输入格式:

第一行输入n,m(1<=m<=n<=10^6)。

接下来一行输入n 个正整数,每个数不超过1000。

输出格式:

输出答案。

输入输出样例:

Maxmin.in maxmin.out

6 3

1 2 3 2 5 4

7

数据范围:

30%的数据满足:1<=m<=n<=500;

50%的数据满足:1<=m<=n<=5000;

100%的数据满足:1<=m<=n<=1000000。

****(考试时的思路)按最平均的方法分子序列,然后把最大的序列和隔壁的换值一直换到不能更小的为止且每次换完都重新排列。看起来很高大上的思想然后0分啦。

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 long long int a[1000001],m,n,left=0,right = 0;
 7 bool check(long long int r)  //判断这个最大值是否合理 
 8 {
 9     long long int all=1,i,sum=0;    
10     for(i = 1;i <= n;i++)
11     {
12         sum += a[i];   //从前往后加,如果大于最大值了则段数加一 求出需要的最少段数 
13         if(sum > r)
14         {
15             all++;
16             if(all > m)    return false;    //如果需要的段数大于要求的段数就表示不满足要求 
17             sum=a[i]; //且新一段就从这个值开始
18         }
19     }
20     return true;  //满足要求 
21 }
22 long long int find(long long int l,long long int r)
23 {
24     long long int i=l,j=r,m;
25     while(i <= j)
26     {
27         m = (i + j) / 2; //二分取中间值,也就相当于新的最大值 
28         if(check(m)==true)  //如果最小值到这个中间值能成立一个划分方法那么下一次搜索最小值到这个中间值再小一点 
29             j = m - 1;
30         else     //如果不成立 
31             i = m + 1;  //下一次搜索后半段。 
32         find(i,j);  //完事儿再接着搜索新的区间 
33     }
34     return i;      //二分结束后输出答案 
35 }
36 int main()
37 {
38     long long int ans,i;
39     scanf("%lld %lld",&n,&m);
40     for(i = 1;i <= n;i++)
41     {
42         scanf("%lld",&a[i]);
43         if(left < a[i])
44             left = a[i];  //LEFT存放子序列的最小值也就是这些单个数组里面的最大值。 
45         right += a[i];   //RIGHT存放子序列最大值也就是所有数相加的和。 
46     }
47     ans = find(left,right); //搜索这个区间里面的值 
48     printf("%lld",ans);
49     return 0;
50 }

***这道题可以用二分动态规划,上面给代码的备注是二分的。备注应该够详细了吧。。

原文地址:https://www.cnblogs.com/rax-/p/8821724.html