题目背景
印度洋暖流温润着纽约,四季丰沛的雨水造就了一望无际的大草原。蒙古包是纽约最独特的一道风景线,每至二月中旬,纽约的土著傣族人民又开始半年一度的转场了。
题目描述
由于牲畜和行李过多,牧民 Azone 不得不多次往返于两个草场之间运输家当。为了顺利转场,Azone 决定花费 ww元津巴布韦币,购买一辆载重为 ww 的汽车。共有 nn 件家具需要搬运,每件家具的重量为 w_iwi 。Azone 每次出发前,会搬若干件总重不超过 ww 的物品上车:出发前,车是空载的,Azone 会选择能搬上车的家具中最重的一件放上车(即该家具之前还未运走且放置该家具后汽车不会超载),然后在剩下的家具中继续选择一件能被搬走的最重的上车,持续装车,直至剩下的家具都塞不上车。装载完毕后,Azone 会开车运走这些家具,卸在目的地,再驾空车返回继续运送,直至转场完毕。
Azone 希望在运送次数不超过 RR 的情况下完成转场,求 Azone 最少需要购置价值多少的车。
输入输出格式
输入格式:
第一行,两个整数 nn 和 RR ,分别表示家具件数及最多运送次数。
第二行,若干个整数 w_iwi ,表示家具重量。
输出格式:
一行,表示答案。
输入输出样例
输入样例#1:
6 2
26 7 10 30 5 4
输出样例#1:
42
说明
对于 100% 的数据, 1 ≤ R,n,w_i ≤ 20001≤R,n,wi≤2000 。
--------------------------------------------------------------------------------------------------------------------------------
再明显不过的二分,可打完二分,提交一看,wa了不少的点。好吧,其实这个二分还是有点难度的。
二分思路即我们每次二分一个w,判断是否合法,即是否可以在不超过r轮运完,如果可以那就尝试更小的,如果不可那就更大点。可这样其实错误的,因为这个w并不一定是严格单调的,即对于w可以,w+1却未必可以。有点难理解,但事实却是这样,可以这样认为:虽然我的空间大了,但是我的策略却还是一成不变的(每次只从最重装起),而我们的这个策略显然不是最优的,即我变大的空间不不一定能够弥补我并不是最优的策略。
那么该怎么做呢?
细想一下,这道题是二分绝对没错,那也就是说一定存在单调的量——对于w可以,那么w+max(wi)也一定可以,其中max(wi)表示最重的家具的重量。
接下来我们来证明它的正确性:
如果w可以,那也就是说,我们每次装的不会超过w,而现在如果w变为了w+max(wi),如果我们每轮装得小于w的话,分两种情况,一种是只剩下这么重的家具了,那么很显然就是装完了,成立;另一种就是装不了了,可这是不可能的,因为当前装了小于w的家具(设为ws),如果装不了也就是说要有一件家具使得ws+wi>w+max(wi),显然不可能,因为ws<w且wi<=max(wi)。换句话说就是我们每轮装得都是>=w的(除了上面讨论的第一种情况,但那也能装完),那么r轮肯定装得完,那么上面的式子就得到了证明。
上面的证明可能没看明白,其实也可自己想一下,还是比较好理解的。
总结:二分除了注意边界还要找一下单调的量,即二分的对象。
code:
#include<iostream>
#include<set>
using namespace std;
const int N = 2e3+10;
multiset<int> s;
int n, r, ans, a[N];
bool check(int ans) {
int i, temp;
s.clear();
for (i = 1; i <= n; i++)
s.insert(a[i]);
for (i = 1; i <= r; i++) {
temp = ans;
while (!s.empty()) {
set<int> ::iterator it = s.upper_bound(temp);
if (it == s.begin()) break;
it--;
temp -= *it;
s.erase(it);
}
if (s.empty()) return true;
}
return false;
}
int main() {
cin >> n >> r;
int i, x;
for (i = 1; i <= n; i++)
cin >> a[i];
int l = 0, r = 1e6;
while (r-l > 1) {
int mid = (l+r)>>1;
if (check(mid)) r = mid;
else l = mid;
}
ans = r;
for (i = r; i >= r-2000 && i>0; i--) {
if (check(i)) ans = i;
}
cout << ans;
}