终于有时间来好好整理一下这几天集训的练习题目了,不得不说集训的强度还是挺大的。 下面我就把题目的链接放上来,自己看吧。
P1182 数列分段 Section II - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这道题还是比较简单的,我们可以发现题目要求的是最大值的最小化。看到这个条件我们很容易就联想到二分答案,毕竟这是二分答案的题的一个重要特征嘛。
现在我们确定好二分的目标:也就是分段后的各个数段和的最大值,那么如何写check()函数呢?
这个问题一般是二分答案的初学者比较困扰的问题,我当初也一样,不过现在已经好很多了,秘诀就是多刷刷题,总结思路。
话说回来,我们可以发现题意要求我们把数列分成m段,注意!!!题目的要求一般指引我们思考的方向,有时实在想不来,多多看题意几遍,你也许就会豁然开朗,写check()函数也就会手到擒来。
我们可以这样想,如果把数列分成m段,那么每一段的和必定要小于或者等于我们的最大值,如果违反了这个条件就与题意矛盾了,所以我们可以一开始把每个数作为一段,先从头取一个数吧,如果他加上数列中的另外一个数(记作A),还是小于我们所选的最大值,那么他加上这个数(也就是A)就是合法的,我们给他加上这个数。如果不合法,也就是大于我们自己所选择的最大值,那么这个数段只能就此终结。
这里注意,这个数段就此终结后,我们需要记录一下数段的个数,最终与m进行比较,如果<=m,说明这个分法是正确的,反之,则是错误的。
下面我把代码贴上来,方便大家理解:
#include<iostream>
using namespace std;
int n, m, a[100100];
bool check(int k) {
int cur = 0, ans = 1;
for (int i = 1; i <= n; i++)
{
if (cur + a[i] <= k)
{
cur += a[i];
}
else {
cur = a[i];
ans++;
}
}
return ans <= m;
}
int main() {
cin >> n >> m;
int l = 0, r = 0,ans = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
r += a[i];
l = max(l, a[i]);//最大值的最小问题,你们最大值的最小就是一个数,且该数是整个序列中最大的
}
while (l < r - 1) {
int mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
}
else {
l = mid;
}
}
if(check(l))
cout << l;
else cout<< r;
return 0;
}
不过这题还有一个比较恶心的点,就是这个二分很容易被卡,只能说太玄学了(没办法,我是蒟蒻)。当初搞了好久才过,如果有卡在这里的可以看看我的码。
就想讲到这里吧,这是我第一次写博客,有哪里不妥之处请大家多多指出!!!