题意:
有个家伙装东西,他的策略是贪心,每次装进去这个盒子能装下的最大的东西,直到把这个盒子装满,再去装下一个盒子。
给出盒子的数量k和一些东西的重量,问你最小需要多大的盒子才能以这种贪心策略装下。
题解:
如果某个解可行,比它大的值未必可行,比如有15个物品,5个39,5个60,5个100,5个盒子,那么盒子大小199可以,200不行,201也不行。所以不能二分。
首先,显然答案下界为ceil(sum/k)。
设最大的物品重量为maxv,假如某个ans装不下,那么在此ans下,装下除maxv外所有物品后,所有箱子的剩余空间都小于maxv
因此,ans*k<sum-maxv+k*maxv
只需在[ceil(sum/k),ceil(sum/k+(k-1)*maxv)]区间内枚举即可。
判断一个ans的可行性复杂度是nlogn
#include<bits/stdc++.h> #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> using namespace std; #define MAXN 1005 #define mem(a,b) memset(a,b,sizeof a) #define rep(i,n,m) for(int i=n;i<=m;++i) const int MOD = 9973; inline int read() { int x = 0, f = 1; char c = getchar(); while (c<'0' || c>'9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return x * f; } int v[MAXN], n, k, num[MAXN], vis[MAXN],num1[MAXN]; bool check(int vlo) { int nu = k; mem(vis, 0); //是否可以装完所有物品,贪心的装,尽量装大的,然后再补漏 int left = n; //rep(i, 1, 1000) num1[i] = num[i]; int maxx = n,no=1; while (nu) { int sp = vlo; for (int i = maxx; i > 0; --i) { if (sp < v[no]) break; if (sp >= v[i] && !vis[i]) { sp -= v[i]; vis[i] = 1; left--; if (!sp) break; } } while (vis[maxx]) maxx--; while (vis[no]) no++; nu--; } if (!left) return 1; return 0; } int main() { int T = read(); for(int tt=1;tt<=T;++tt) { int sum = 0; n = read(), k = read(); mem(num, 0); mem(num1, 0); rep(i, 1, n) v[i] = read(), sum += v[i]; sort(v + 1, v + 1 + n); int bi = sum / k; if (sum % k) bi++; int beg = max(v[n], bi); int ans=beg; for (int i = beg; i <= 1000000; ++i) { if (check(i)) { ans = i; break; } } printf("Case #%d: %d ", tt,ans); } }