题意
给一个只包含 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<mini∣∣k>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
k−i,那么当前从
a
a
a中能移动的
1
1
1最多为
m
a
x
i
−
(
k
−
i
)
maxi-(k-i)
maxi−(k−i),那么首先从
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()<maxi−k+i,那么移动所有的
p
p
p,继续下一个循环;如果
p
.
s
i
z
e
(
)
>
=
m
a
x
i
−
k
+
i
p.size() >=maxi-k+i
p.size()>=maxi−k+i,证明当前最多只能移动
m
a
x
i
−
k
+
i
maxi-k+i
maxi−k+i步了,然后剩下
k
−
i
k-i
k−i的时间刚好可以每次移动一步。
#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';
}
}