UVa10163 Storage Keepers
AC以后看了看, 网上基本上都是用二次dp做的.
实际上,看到本题的第一问:“使仓库的最小安全系数最大”,我的第一想法就是二分。
二分寻找前驱。设 mid 为当前二分的中点。
于是,我们只需验证能不能满足 所有的安全值都大于等于mid 这个条件即可。
而这个条件是非常好验证的。相信做过二分答案相关题目的人都能理解。
对于第二问,还是采用01背包的方法。(貌似也没有别方法)。
1 #include <cstdio>
2 #include <algorithm>
3 #include <iostream>
4 #include <cstring>
5 using namespace std;
6 const int MAXN = 100 + 10;
7 const int MAXM = 30 + 5;
8 const int INF = 0x3f3f3f3f;
9
10 int N, M;
11 int p[MAXM];
12
13 inline bool check(int x)
14 {
15 register int cnt = 0, a = 0;
16
17 for(int i = 1; i <= M; i++)
18 {
19 a = 1;
20 while((p[i] / a) >= x) a++;
21 a--;
22 cnt += a;
23 }
24
25 return cnt >= N;
26 }
27
28 int f[1010];
29 inline int getans(int x)
30 {
31 if(x == 0) return 0;
32 memset(f, 0x3f, sizeof(f));
33 f[0] = 0;
34 for(int i = 1; i <= M; i++)
35 {
36 if(p[i] < x) continue;
37 int v = p[i] / x;
38 for(int j = 1000; j >= v; j--)
39 f[j] = min(f[j], f[j - v] + p[i]);
40 }
41
42 int ans = INF;
43 for(int i = N; i <= 1000; i++)
44 ans = min(ans, f[i]);
45 return ans == INF ? 0 : ans;
46 }
47
48 int main()
49 {
50 //freopen("10163.in", "r", stdin);
51 //freopen("10163.out", "w", stdout);
52 while(cin>>N>>M, N != 0)
53 {
54 memset(p, 0, sizeof(p));
55 register int l = 0, r = 0;
56 for(int i = 1; i <= M; i++)
57 scanf(" %d", &p[i]),
58 r += p[i];
59
60 while(l < r)
61 {
62 int mid = (l + r + 1) / 2;
63 if(check(mid)) l = mid;
64 else r = mid - 1;
65 }
66 printf("%d %d\n", l, getans(l));
67 }
68 return 0;
69 }