Codeforces Round #632,D. Challenges in school №41, 思维+二进制序列01的交换

题目链接

题意

给一个只包含 0 , 1 0,1 0,1的序列,如果 a [ i ] = 1 , a [ i + 1 ] = 0 a[i]=1,a[i+1]=0 a[i]=1,a[i+1]=0,那么在一次操作中可以将 1 1 1 0 0 0交换,每一秒至少进行一次这样的操作,也可以在一秒内进行多次这样的操作,问在第 k k k秒能否让序列刚好变成 000111 000111 000111这种所有 0 0 0在前, 1 1 1在后的序列。并且输出每秒的操作的位置。

题解

首先考虑把变换一个序列到最终形式所需要的最少的时间 m i n i mini mini和最大的时间 m a x i maxi maxi
最大的时间就是每次仅交换一对 10 10 10
最小的时间就是每一次交换所有可交换的 10 10 10

最大的时间很好求,在每一次遇到 1 1 1时,加上这个 1 1 1之后的 0 0 0的数量就可以了。
最小的时间,就是当前 1 1 1 0 0 0的数量与(后一个 1 1 1的时间+1)的最大值,这么说不是很好理解。
举个例子:比如
1101000 1101000 1101000,相应位置上的最小时间为
54    3 54\space\ 3 54  3
而要取最大值,比如这个序列:
110011110 110011110 110011110
65      4321 65\space\space\space\ 4321 65    4321
也就是前面的 1 1 1与后面的 1 1 1同时后移时,前面的 1 1 1会不会被后面的 1 1 1所阻塞。

如果 k < m i n i ∣ ∣ k > m a x i k<mini || k>maxi k<minik>maxi,则直接输出不存在。

现在我们知道了 k k k在区间 [ m i n i , m a x i ] [mini,maxi] [mini,maxi]内,那么比较好的想法就是首先同步移动所有能移动的 1 1 1,从而能得到一个 k ′ = m a x i ′ k'=maxi' k=maxi,那么剩下的只需要一步一步移动即可。
现在我们开始尝试每次移动最多的 1 1 1,假设当前序列为 a a a,求得的最大时间 m a x i maxi maxi,当前时间为 i i i,那么剩余时间为 k − i k-i ki,那么当前从 a a a中能移动的 1 1 1最多为 m a x i − ( k − i ) maxi-(k-i) maxi(ki),那么首先从 a a a中找到所有能移动的 1 1 1的位置 p p p,如果 p . s i z e ( ) < m a x i − k + i p.size()<maxi-k+i p.size()<maxik+i,那么移动所有的 p p p,继续下一个循环;如果 p . s i z e ( ) > = m a x i − k + i p.size() >=maxi-k+i p.size()>=maxik+i,证明当前最多只能移动 m a x i − k + i maxi-k+i maxik+i步了,然后剩下 k − i k-i ki的时间刚好可以每次移动一步。

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

int n, k;
const int maxn = 3e3 + 10;
char s[maxn];

vector<int> find_steps() {
	vector<int> steps;
	for (int i = 1; i <= n-1; ++i) {
		if (s[i] == 1 && s[i+1] == 0) {
			steps.push_back(i);
		}
	}
	return steps;
}

int main() {
	cin.tie(0);
	ios_base::sync_with_stdio(false);
	cin >> n >> k;
	cin >> s+1;
	for (int i = 1; i <= n; i++) {
		s[i] = (s[i] == 'L' ? 0 : 1);
	}
	
	int maxi = 0, mini = 0, cnt = 0, last = -1;
	for (int i = n; i >= 1; --i) {
		if (s[i] == 0) {
			++cnt;
		} else {
			if (cnt == 0) continue;
			maxi += cnt;
			mini = max(cnt, last+1);
			last = mini;
		}
	}
	
	if (k < mini || k > maxi) {
		cout << -1 << endl;
		return 0;
	}
	
	bool is_min = false;
	vector<int> one_step;
	for (int i = 1; i <= k; ++i) {
		if (!is_min) {
			auto steps = find_steps();
			// 当前最多能移动maxi步
			// 但是其中的k-i都是只能变化一次的 
			// 当前一步能变化steps.size()次
			cout << min(int(steps.size()), maxi-(k-i)) << " ";
			int cur = 0;
			while (k-i < maxi && cur < steps.size()) {
				int now = steps[cur];
				cout << now << " ";
				s[now] = 0; s[now+1] = 1;
				++cur;
				--maxi;
			}
			
			if (k-i == maxi) {
				is_min = true;
				one_step = find_steps();
			}
		} else {
			cout << "1 ";
			int now = one_step.back();
			one_step.pop_back();
			cout << now;
			s[now] = 0; s[now+1] = 1;
			
			if (now-1 >= 1 && s[now-1] == 1) {
				one_step.push_back(now-1);
			}
			if (now+2 <= n && s[now+2] == 0) {
				one_step.push_back(now+1);
			}
		}
		cout << '\n';
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值