B. Partial Replacement
题意:有字符串由.
和*
组成,可标记其中*
,首尾的*
必须被标记,使被标记的*
之间距离不超过k
,求最少的标记量
思路:首先从首尾出发确定首尾*
的位置,再由首beg
出发向后的k个元素里,将最靠右的*
标记
选择最右的标记为了更快地抵达尾
ed
,该局部最优可推广到全局最优
将刚标记的*
作为新的起点,再在向后k
个元素里标记最右的*
,重复执行上述步骤直至抵达尾ed
为止
#include <bits/stdc++.h>
#define fastio() ios_base::sync_with_stdio(0);cin.tie(0)
using namespace std;
int main()
{fastio();int t;cin >> t;while (t--){int n, k;cin >> n >> k;string s;cin >> s;int beg = 0, ed = n - 1;while (s[beg] != '*') ++beg;while (s[ed] != '*') --ed;int ans = 2;if (beg == ed) ans--;for (int i = beg;;){int j = i + k;if (j >= ed) break;while (s[j] != '*') --j;ans++;i = j;}cout << ans << "\n";}return 0;
}
D. Epic Transformation
题意:对于整型数组内,每一对不相等的元素可以进行消除,试求可将数组消除到剩余尽可能少元素的数量
思路:首先将数组排序,将相同的元素聚集在一起,再将数组从正中间分为左右两部分,假如「没有相同的值跨越中线」(例如:1122 | 3344),即左边的任意值在右边找不到与其相等的值,则每次消除分别从左右两边各选任意一个元素即可,最后达到两边全部消除;若有相同值跨越中线
记跨越中线的值为R
,首先将左边的R
与右边的非R
匹配消除、将右边的R
与左边的非R
匹配消除,若此时R
被消除完,则说明剩余的状态可归类为「没有相同的值跨越中线」,即最终可以全部消除完,而若仍有R
剩余(此时其他均已被消除),其实只剩下未被消除的R
(此时无法继续消除),因此剩余R
的数量即为所求答案。当然,当数组元素数量为奇数时,正中间的数最后一定会剩余,加以讨论即可。
#include <bits/stdc++.h>
#define fastio() ios_base::sync_with_stdio(0);cin.tie(0)
using namespace std;
int main()
{fastio();int t;cin >> t;while (t--){int n;cin >> n;vector<int> v(n);for (int i = 0; i < n; i++) cin >> v[i];sort(v.begin(), v.end());int ans = n & 1 ? 1 : 0;int mid_pos = n / 2;int mid_elem = v[mid_pos];int left_me_cnt = 0, right_me_cnt = 0, half_cnt = n / 2;int left_pos, right_pos;if (n & 1){left_pos = mid_pos - 1;right_pos = mid_pos + 1;}else{left_pos = mid_pos - 1;right_pos = mid_pos;}while (left_pos >= 0 && v[left_pos] == mid_elem) left_me_cnt++, left_pos--;while (right_pos < n && v[right_pos] == mid_elem) right_me_cnt++, right_pos++;ans += 2 * max(0, right_me_cnt - (half_cnt - left_me_cnt));cout << ans << "\n";}return 0;
}
E. Restoring the Permutation
题意:原始数组A
为长度为n
,由1
-n
元素组成,现给出它的「当前最大值数组」B
,即有 bi=max(a1,a2,...,ai)b_i = max(a_1,a_2,...,a_i)bi=max(a1,a2,...,ai),例如:A{3,1,4,2,7,5,6}
则有B{3,3,4,4,7,7,7}
,现在由给出的「当前最大值数组」求可能的「最小序原始数组」和「最大序原始数组」,大小序可将上面数组A{3,1,4,2,7,5,6}
理解成整型 3142756
再比大小,即越靠前的元素权值越重
思路:
- 「最小序原始数组」
其实可以理解为当cur_max
数组刚发生变化时,该位置对应的结果便为当前的最大值,而后若cur_max
未发生变化则由小到大将序列元素依次填入即可(思路即是:使越靠前的位置放置越小的元素),由于是由小到大选元素,这样的选取总能使结果数组满足对应的「当前最大值数组」
- 「最大序原始数组」
总体思路与前者相反,即使越靠前的位置放置尽可能大的元素,当我们考虑第二个元素的选择的时候:当然可选的最大元素为 7
,但此时cur_max[2] = 3
,说明我们只能考虑从不大于3
的元素选取,而3
被第一个元素选了,因此尽可能大的选择是2
。于是更详细的思路是:「每次选择不大于cur_max[i]
的尽可能大的数」
这里通过栈结构来完成此过程:
- 首先读到
cur_max[0] = 3
,将[1,3][1,3][1,3]依次入栈,再对于此后cur_max[i] == 3
依次取栈顶元素并出栈(这样保证了「每次选择不大于cur_max[i]
的尽可能大的数」),到了cur_max[3] = 4
,此时栈中还剩余元素1
cur_max[3] = 4
,将[3+1,4][3 +1,4][3+1,4] (其实只有4
本身)依次入栈,和上述同样的操作,读到cur_max[5] = 7
,此时栈为空
cur_max[5] = 7
,将[4+1,7][4+1,7][4+1,7]依次入栈,最后再依次出栈作为结果
#include <bits/stdc++.h>
#define fastio() ios_base::sync_with_stdio(0);cin.tie(0)
using namespace std;
int main()
{fastio();int t;cin >> t;while (t--){int n;cin >> n;vector<int> q(n);for (int i = 0; i < n; i++) cin >> q[i];;vector<int> used(n + 1);vector<int> min_ans(n);int pos = 1;min_ans[0] = q[0], used[q[0]] = 1;for (int i = 1; i < n; i++){if (q[i] == q[i - 1]){while (used[pos]) ++pos;min_ans[i] = pos;used[pos] = 1;}else{min_ans[i] = q[i];used[q[i]] = 1;}}for (int i = 0; i < n - 1; i++) cout << min_ans[i] << " ";cout << min_ans[n - 1] << "\n";vector<int> max_ans(n);stack<int> st;int pre_val = 0;for (int i = pre_val + 1; i <= q[0]; i++) st.push(i);max_ans[0] = st.top(); st.pop();for (int i = 1; i < n; i++){if (q[i] == q[i - 1]){max_ans[i] = st.top(); st.pop();}else{pre_val = q[i - 1];for (int j = pre_val + 1; j <= q[i]; j++) st.push(j);max_ans[i] = st.top(); st.pop();}} for (int i = 0; i < n - 1; i++) cout << max_ans[i] << " ";cout << max_ans[n - 1] << "\n";}return 0;
}