【竞赛题解】Codeforces Round #710 (Div. 3)

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]的尽可能大的数」

这里通过结构来完成此过程:

  1. 首先读到cur_max[0] = 3,将 [ 1 , 3 ] [1,3] [1,3]依次入栈,再对于此后cur_max[i] == 3依次取栈顶元素并出栈(这样保证了「每次选择不大于cur_max[i]的尽可能大的数」),到了cur_max[3] = 4,此时栈中还剩余元素 1

在这里插入图片描述

  1. cur_max[3] = 4,将 [ 3 + 1 , 4 ] [3 +1,4] [3+1,4] (其实只有4本身)依次入栈,和上述同样的操作,读到cur_max[5] = 7,此时栈为空

在这里插入图片描述

  1. 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值