DP题组
按照顺序来。
大意:
给你一个集合,求其所有非空子集的权值的中位数。
某集合的权值即为其元素之和。
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 }
大意:
输入 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 }
大意:
在 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 }
大意:
给定 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 }
不会...