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 }

 

posted @ 2018-05-22 15:41  俺是小程  阅读(131)  评论(0编辑  收藏  举报