DP题组

按照顺序来。

Median Sum

大意:

给你一个集合,求其所有非空子集的权值的中位数。

某集合的权值即为其元素之和。

1 <= n <= 2000 

解:

集合配对,每个集合都配对它的补集。

最大的那个没有配对,所以求(原集合的权值 + 1) >> 1,不小于这个的第一个即为所求。

用bitset实现可行性背包。

 1 #include <cstdio>
 2 #include <bitset>
 3 
 4 const int N = 2010;
 5 
 6 std::bitset<N * N> bt;
 7 
 8 int a[N];
 9 
10 int main() {
11     int n, tot = 0;
12     scanf("%d", &n);
13     for(int i = 1; i <= n; i++) {
14         scanf("%d", &a[i]);
15     }
16     for(int i = 1; i <= n; i++) {
17         bt |= (bt << a[i]);
18         bt[a[i]] = 1;
19         tot += a[i];
20     }
21     tot = (tot + 1) >> 1;
22     while(bt[tot] == 0) {
23         tot++;
24     }
25     printf("%d", tot);
26     return 0;
27 }
AC代码

No Need

大意:

输入 N 个数字和 K 。

如果对于所有包含 Ai ,且和不小于 K 的集合,去掉 Ai 后和还不小于 K ,那么 Ai 就是 no need 的。

问有多少个元素是 no need 的。

1 <= n, k <= 5000 

解:

no need 的一定是连续最小的一段,证明如下:

若 x need,且 y > x

对于每个包含 x 且 >= K 的集合,去掉 x 一定小于 K 。

若此集合包含 y ,则去掉 y 也小于 K 。

若此集合不包含 y ,则把 x 换成 y 即可。

这样证明了所有含x的集合。

对于某些把 y 换成 x 就会小于 K 的集合,

把 y 换成 0 也小于 K 。

证毕。

然后二分check。

check函数用上一题的思想,对于 x ,只要去掉 x 的集合和没有在[k - x, k)之间的,x 即为 no need

若 x >= k,单 x 元素即为所求,x 为 need。

 1 #include <cstdio>
 2 #include <bitset>
 3 #include <algorithm>
 4 
 5 const int N = 5010;
 6 
 7 std::bitset<N> bt;
 8 
 9 int n, a[N], k;
10 
11 inline bool check(int x) {
12     if(a[x] >= k) {
13         return 1;
14     }
15     bt.reset();
16     for(int i = 1; i <= n; i++) {
17         if(i == x) {
18             continue;
19         }
20         if(a[i] > N - 1) {
21             break;
22         }
23         bt |= (bt << a[i]);
24         bt.set(a[i]);
25     }
26     for(int i = k - a[x]; i < k; i++) {
27         if(bt[i]) {
28             return 1;
29         }
30     }
31     return 0;
32 }
33 
34 int main() {
35     scanf("%d%d", &n, &k);
36     for(int i = 1; i <= n; i++) {
37         scanf("%d", &a[i]);
38     }
39     std::sort(a + 1, a + n + 1);
40     int l = 1, r = n, mid;
41     while(l < r) {
42         mid = (l + r + 1) >> 1;
43         if(check(mid)) {
44             r = mid - 1;
45         }
46         else {
47             l = mid;
48         }
49     }
50     if(r == 1 && check(1)) {
51         r = 0;
52     }
53     printf("%d", r);
54     return 0;
55 }
AC代码

すぬけ君の塗り絵 / Snuke's Coloring

大意:

在 n * m 的区域中,输入 k 个黑格子的位置(x,y)。
对于每个 3 * 3 的区域,会包含 0 到 9 个黑格子。
求包含 0 ~ 9 个黑格子的 3 * 3 的区域各有多少个?

1 <= m, n <= 10 ^ 9

0 <= k <= 10 ^ 5

解:

每个黑格子只会对 9 个 3 * 3 的区域产生影响。

注意map的用法: it -> second

 1 #include <cstdio>
 2 #include <map>
 3 #include <algorithm>
 4 
 5 typedef long long LL;
 6 
 7 const int N = 100010;
 8 
 9 struct A {
10     int x, y;
11     A(int x = 0, int y = 0) {
12         this->x = x;
13         this->y = y;
14     }
15     bool operator < (const A &d) const {
16         if(x == d.x) {
17             return y < d.y;
18         }
19         return x < d.x;
20     }
21     bool operator == (const A &d) const {
22         return x == d.x && y == d.y;
23     }
24 }a[N * 9];
25 
26 std::map<A, int> mp;
27 
28 int ans[10];
29 
30 int main() {
31     int m, n, k;
32     scanf("%d%d%d", &n, &m, &k);
33     for(int i = 1, x, y; i <= k; i++) {
34         scanf("%d%d", &x, &y);
35         for(int j = 0; j < 3; j++) {
36             for(int k = 0; k < 3; k++) {
37                 if(x + j > n || y + k > m || x + j < 3 || y + k < 3) {
38                     continue;
39                 }
40                 mp[A(x + j, y + k)]++;
41             }
42         }
43     }
44     std::map<A, int>::iterator it = mp.begin();
45     int tot = 0;
46     for(; it != mp.end(); it++) {
47         tot++;
48         ans[it->second]++;
49     }
50     printf("%lld\n", 1ll * (m - 2) * (n - 2) - 1ll * tot);
51     for(int i = 1; i <= 9; i++) {
52         printf("%d\n", ans[i]);
53     }
54     return 0;
55 }
AC代码

Largest Smallest Cyclic Shift

大意:

给定 n 个 a , m 个 b, k 个 c

求所组成的字符串的最小循环表示法的最大字典序。

解(结论):把这些一个一个的字符放入multiset,每次取字典序最大最小的合并放回去。

最后的即为所求。

 1 #include <cstdio>
 2 #include <set>
 3 #include <iostream>
 4 
 5 using std::string;
 6 
 7 std::multiset<string> s;
 8 
 9 int main() {
10     int n;
11     scanf("%d", &n);
12     for(int i = 1; i <= n; i++) {
13         s.insert("a");
14     }
15     scanf("%d", &n);
16     for(int i = 1; i <= n; i++) {
17         s.insert("b");
18     }
19     scanf("%d", &n);
20     for(int i = 1; i <= n; i++) {
21         s.insert("c");
22     }
23     while(s.size() > 1) {
24         string a = *s.begin();
25         string b = *(--s.end());
26         s.erase(s.begin());
27         s.erase(--s.end());
28         s.insert((string)(a + b));
29     }
30     std::cout << *s.begin();
31     return 0;
32 }
AC代码

Popping Balls

不会...

 

posted @ 2018-07-11 15:06  garage  阅读(144)  评论(0编辑  收藏  举报