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
,即有
b
i
=
m
a
x
(
a
1
,
a
2
,
.
.
.
,
a
i
)
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;
}