這道題讓最大值最小, 顯然是二分答案
當題目求的是最大值最小, 最小值最大, 這個時候就要想到二分答案
為什么可以二分答案呢, 因為這個時候解是單調性的, 如果簡單粗暴一點
就全部枚舉一遍, 驗證答案。但是因為答案滿足單調性, 可以用二分的方法
來”枚舉“, 復雜度可以從n降到logn
開始我自己寫了一個, 但是WA, 后來看了劉汝佳的代碼, 發現要注意三點
(1)這道題的和的最大值會爆int, 要用long long。
養成看到題目的時候計算最大值看會不會爆int的習慣(int最大大概是2乘10的9次方)
(2)輸出的時候,因為是前面的子序列的和盡量小, 所以我自己寫的時候想到了從后
往前盡量取(貪心)來輸出, 但是沒有考慮到分成固定要分成k個。 所以要專門用一個remain
來控制分成子序列的個數, 不然子序列會分少。
(3) 二分開始時候的左端點一定要設為元素最大值, 我一開始有想到, 但是覺得好像
對答案沒有什么影響, 就懶得去求最大值, 就直接設為0, 然后就WA了。
事實上, 在判斷這個答案是否符合的時候(我的程序中的judge函數),這個key值
根本就小于元素的時候, 是可以通過的, 這個錯誤是非常難發現的, 所以要提前
處理, 也就是在一開始的時候最小的可能的答案就設為元素最大值
順便提一下, 我的程序中l--, 是因為我的二分的寫法是這么寫的。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;typedef long long ll;
const int MAXN = 512;
int a[MAXN], board[MAXN], n, k;bool judge(ll key)
{ll num = 1, sum = 0;REP(i, 0, n){if(sum + a[i] <= key) sum += a[i];else {num++; sum = a[i];if(num > k) return false;}}return true;
}void print(ll key)
{memset(board, 0, sizeof(board));ll sum = 0, remain = k;for(int i = n - 1; i >= 0; i--){if(sum + a[i] > key || i + 1 < remain) sum = a[i], board[i+1] = 1, remain--;else sum += a[i];}printf("%d", a[0]);REP(i, 1, n){if(board[i]) printf(" /");printf(" %d", a[i]); }puts("");
}int main()
{int T;scanf("%d", &T);while(T--){ll l = 0, r = 0;scanf("%d%d", &n, &k);REP(i, 0, n) scanf("%d", &a[i]), r += a[i], l = max(l, (ll)a[i]);l--;while(l + 1 < r){ll mid = (l + r) / 2;if(judge(mid)) r = mid;else l = mid;}print(r);}return 0;
}