比较经典的一个题目,考察的既不是滑动窗口也不是动态规划,考察的其实是二分法,二分的范围是从序列中的最大值到序列的所有的元素之和。每次二分计算出对应的mid值,然后判断是否能够将序列划分成为不超过k个部分,每个部分的值都不超过mid,如果可以,那么就继续缩小范围,也就是降低下限进行判断,如果不可以的话,那么就放大范围也就是提高下限进行判断,得出最终的结果之后,就开始对数列进行划分,这里要注意一个点,题目要求越靠近前方的部分的和就要越小,所以我们从后向前加‘/’,同时要注意剩余的斜杠数等于剩下的为划分的数字的个数。其他的就比较简单了,具体实现见如下代码:
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
#include<functional>
using namespace std;
int N;
typedef long long LL;
vector<LL> data;
vector<int> ind;
int solve(LL up){
int ans = 1;
LL temp = 0;
for (int i = 0; i < data.size(); i++){
if (temp + data[i] <= up){
temp += data[i];
}
else{
ans++;
temp = data[i];
}
}
return ans;
}
void getRes(LL up,int k){
LL temp = 0;
for (int i = data.size() - 1; i >= 0; i--){
if (temp + data[i] > up || i + 1 < k){
ind[i] = 1;
k--;
temp = data[i];
}
else
temp += data[i];
}
for (int i = 0; i < data.size() - 1; i++){
cout << data[i] << " ";
if (ind[i]) cout << "/ ";
}
cout << data[data.size() - 1] << endl;
}
int main(){
cin >> N;
while (N--){
int m, k;
cin >> m >> k;
data.clear();
ind.clear();
LL sum = 0;
LL maxn = -1;
for (int i = 0; i < m; i++){
long long t;
cin >> t;
data.push_back(t);
ind.push_back(0);
sum += t;
maxn = max(maxn, t);
}
LL L = maxn, R = sum;
while (L < R){
LL mid = L + (R - L) / 2;
if (solve(mid) <= k) R = mid;
else L = mid + 1;
}
getRes(L,k);
}
return 0;
}