题目连接:洛谷P1182
题意就是说将一个长度为N的连续序列换分为M个连续的子序列。使得每个序列之和的最大值最小。
解法:
看了很多篇题解,解法就是利用二分法,这道题要注意上下界,下界应该是所有数的最大值(即所有区间只有一个元素的时候),上界应该是所有数之和(即整个序列为一个区间)。最开始把下界理解为所有数的最小值,然后WA了一个点。
这种类型的模板题可以这么来看:因为目的是要让所有区间的最大值最小,mid相当于我们二分搜索的目标值,在judge()函数里,我们要出求当前目标值为mid时,可以划分为几个区间,所以此时每个区间和应该尽量逼近mid,即<=mid。若cnt<m,表示mid值太大,导致划分的区间个数太少,应该在[left,mid-1]中继续搜索可行解;若cnt==m,表示这已经是满足题意的一个可行解,应该是要使mid尽量小,我们是否可以在[left,mid-1]中继续搜索更优的解呢?因为在试探更优的解的时候变的是right的值,所以当right<left时(这个时候right+1=left),left的值即为最后一次满足条件的最优解。所以最后我们返回left的值。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<sstream>
using namespace std;
int a[100005];
int n, m;
bool judge(int mid) { //mid表示当前二分时刻所有子区间之和的最大值
int sum = 0;
int cnt = 1; //cnt:当前子区间之和<=mid的区间个数
for (int i = 1; i <= n; i++) {
if (sum + a[i] <= mid)
sum += a[i];
else {
cnt++;
sum = a[i];
}
}
return cnt <= m; //若区间个数小于等于m,返回真,说明mid可以更小
}
int main()
{
scanf("%d%d", &n, &m);
int sum = 0;
int maxnum = -1;
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
sum += a[i];
maxnum = max(maxnum, a[i]);
}
int left = maxnum; //下界
int right = sum; //上界
while (left <= right) {
int mid = (left + right) / 2;
if (judge(mid)) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
cout << left; //因为是求最小,所以返回left
return 0;
}
洛谷P2678 跳石头这是二分查找的模板题,题意是,在两个石头之间有n个石头,从中拿走m个石头,使得剩下的石头连续两个之间的最短距离最大,它与上题不同的是求最小距离的最大值。
这道题的下界是0,上界是L。cnt记录可以拿走石头的个数,这题我有个疑问,为什么不用判断第最后一个石头与上一个石头之间的距离?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<sstream>
using namespace std;
int a[50005];
int l, n, m;
bool judge(int mid) {
int last = 0;
int cnt = 0;
for (int i = 1; i <= n;i++) {
if (a[i] - last < mid)
cnt++;
else
last = a[i];
}
return cnt <= m;
}
int main()
{
scanf("%d%d%d", &l, &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", a + i);
a[n + 1] = l;
int left = 0;
int right = l;
while (left <= right) {
int mid = (left + right) / 2;
if (judge(mid)) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
cout << right;
return 0;
}