洛谷——P1182数列分段

这道题本质上是用到了二分+贪心。
先介绍一下贪心。贪心解决的是局部最优解问题,但是如果一个问题每一个的局部的最优解对下一个局部不造成影响,显然这样就成为了全局最优解。那么对于此题是否适用呢?显然,这个题的最优解a是数列中的最大值。而最糟糕的情况b是只能划分为一段,这样就是数列之和。我们不妨从a枚举到b,考虑每一个值是否可行。那么判断可行的条件是什么呢?我们以输入样例为例。4,2,4,5,1。如果选择7,2和4之间要划分一次,4和5之间要划分一次,至少划分2次。这样意味着可以进一步划分,而进一步划分的结果是让每一段和不变大,甚至还能变小。反之,如果划分次数大于要求,也就只能让枚举递增。
不过,这样显然效率太低。根据上面的分析,我们显然可以用二分法解决问题。
这样其实已经可以解决问题了。不过每一次判断累加有些繁琐,这样我们就可以用前缀和解决。还以样例为例,4,2,4,5,1。我们改动一下。0,4,6,10,15,16。每一位是前面加上自身的和。这样,我们求每一段之和,就可以直接作差相减。比如第2位数加上第3位数,就可以表示为10-4。
代码如下:

#include<iostream>
using namespace std;
int a[100001];
int N;
int c;
int max(int a, int b) {
 return a > b ? a : b;
}
bool check(int x) {
 int o = 0;
 for (int i = 1, j = 0; i <= N; i++) {
  if (a[i] - a[j] > x) {
   o++;
   j = i - 1;
   i--;
  }
 }
 return o <= c - 1;
}
int main()
{
 int l = 0;
 a[0] = 0;
 cin >> N >> c;
 for (int i = 1; i <= N; i++) {
  int t;
  cin >> t;
  a[i] = a[i - 1] + t;
  l = max(l, t);
 }
 int r = a[N];
 while (l < r) {
  int mid = l + (r - l) / 2;
  if (check(mid))
   r = mid;
  else l = mid + 1;
 }
 cout << l;
 return 0;
}
发布了14 篇原创文章 · 获赞 3 · 访问量 146

猜你喜欢

转载自blog.csdn.net/IMDASHUAI/article/details/104961914
今日推荐