D. Binary String To Subsequences #贪心 #构造
题意
给定一个01串s,完全分割成若干子序列(注意,不要混淆子串与子序列的概念),其中的子序列不包含两个相邻的0或1(eg:"0101","1010")。对s按这样的分割方式,最少能分出多少串子序列?同时,还要求输出s串中每一字符所在的分割出的子序列编号
分析
参考了官方题解的思路,我们可以定义两个数组为now0和now1,now0存下了当前以0为结尾的所有10子序列,now1反之。
假定我们遇到str[i]为’0’,为了使分割的子序列数量越少越好,当然是希望能够将这个字符’0’接入之前已创建好的子序列中,因此先检查now1是否存在以1结尾的10子序列。
如果存在,我们就接入(注意,接入后,该子序列的末尾是’0’了,因而需要在now0数组留下标记);如果不存在,我们只能从now0中创建一个新编号,注意这个编号是基于所有子序列的数量的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <string> #include <cstdio> #include <iostream> #include <vector> #include <string> using namespace std; typedef long long ll; int main(){ int q; scanf("%d", &q); while(q--){ vector<int> ans; vector<int> now1, now0; int n; scanf("%d", &n); string str; cin >> str; for (int i = 0; i < n; i++){ int newkey = now0.size() + now1.size(); if(str[i] == '0'){ if(now1.empty()){ now0.push_back(newkey); } else{ newkey = now1.back(); now0.push_back(newkey); now1.pop_back(); } } else { if(now0.empty()){ now1.push_back(newkey); } else{ newkey = now0.back(); now1.push_back(newkey); now0.pop_back(); } } ans.push_back(newkey); } printf("%d\n", (int)(now1.size() + now0.size())); for (int i = 0; i < n; i++){ printf("%d%c", ans[i] + 1, (i == n - 1) ? '\n' : ' '); } } }
|
其实for循环内部不需要分出两个大if判断,可以定义now[2][...]然后通过now[!newkey]进行一系列操作即可
E1. Weights Division #贪心 #大顶堆
题意
给定每条边带一定权值的有根树。你可以将任意一条边的权值减少一半,即wi:=wi/2(向下取整),每一次对某一边权值减少一半记为一次操作。定义树的总重量:根节点到所有叶子的路径权值之和。现给定S,需要你用最少的操作次数使得树的总重量不超过S。
分析
根节点到达某一叶子的路径,与 根节点到达另一叶子的路径,很有可能发生部分重合。而题干中不遗漏路径中所有经过边的权值,所以我们需要提前将任何边的经过次数num统计下来。
我们很容易往贪心的方向考虑。贪心什么?我最初贪心,是将当前所有边中,出现次数×边权得到的加权最大的边挑出来并进行修改。然而,这样不准确,比如{w1=5,num1=4}与{w2=2,num2=10},他们总加权相等,但由于边权值缩小是向下取整,5缩小一半对总权重的影响比2缩小一半更大。从这里我们可以发现,贪心的目标应该是选择缩小对树重量的影响最大的边即缩小前与缩小后之差最大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <string> #include <cstdio> #include <iostream> #include <queue> using namespace std; typedef long long ll; const int MAXN = 2e5 + 5; int H[MAXN], tot = 0; ll s = 0, n, cursum = 0; typedef struct Edge { int to, nextNbr, num; ll w; bool operator < (const struct Edge& x) const { return num * (w - w / 2) < x.num * (x.w - x.w / 2); } }Edge; Edge E[MAXN * 2]; priority_queue<Edge> myque; void addEdge(int u, int v, ll w) { tot++; E[tot].to = v; E[tot].w = w; E[tot].nextNbr = H[u]; E[tot].num = 1; H[u] = tot; } void Init() { for (int i = 1; i <= n; i++) H[i] = -1; tot = 0; cursum = 0; while (!myque.empty()) myque.pop(); } int dfs(int now, int pre) { int cnt = 0; bool f = false; for (int i = H[now]; i >= 0; i = E[i].nextNbr) { if (E[i].to != pre) { E[i].num = dfs(E[i].to, now); cnt += E[i].num; myque.push(E[i]); cursum += (ll)E[i].num * E[i].w; f = true; } } return f ? cnt : 1; } int main() { int q; scanf("%d", &q); while (q--) { scanf("%lld%lld", &n, &s); Init(); for (int i = 1; i <= n-1; i++) { int u, v; ll w; scanf("%d%d%lld", &u, &v, &w); addEdge(u, v, w); addEdge(v, u, w); } dfs(1, 1); ll ans = 0; while (cursum > s) { Edge tmp = myque.top(); myque.pop();
cursum -= tmp.w * tmp.num; tmp.w = (ll)(tmp.w / 2); cursum += tmp.w * tmp.num;
myque.push(tmp); ans++; } printf("%lld\n", ans); } return 0; }
|