CodeTON Round #7 (Div. 1 + Div. 2)
A.jagged Swaps
题意:
给出一个包含 n n n个数字的序列,每次可以选择一个同时大于左右两边相邻的数字,将这个数字与它右边的数字交换,问能否在经过若干次操作后使序列变为升序。
分析:
由于交换只能向后进行,且第一个元素无法向后交换(不存在左边的数字),而其他大的数字均可以通过交换到达自己的位置,因此只需要考虑开始时序列的第一个数字是否为1,如果是1,就是"YES"
,否则,就是"NO"
。
hint:包含 n n n个数字的序列恰好包含 1 ∼ n 1 \sim n 1∼n中每一个数字。
代码:
#include <bits/stdc++.h>
using namespace std;
int a[15];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
if (a[1] != 1) {
cout << "NO" << endl;
} else {
cout << "YES" << endl;
}
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
B.AB Flipping
题意:
给出一个长度为
n
n
n且仅包含"AB"两种字符的字符串,每次可以选择一个下标
i
i
i,当字符串中第
i
i
i个字符为'A'
,且第
i
+
1
i + 1
i+1个字符为'B'
,那么可以让第
i
i
i个字符和第
i
+
1
i + 1
i+1个字符交换。
要求每个下标 i i i均只能选择一次,问最多可以进行多少次交换。
分析:
当出现连续的'B'
前面有一个'A'
时,这段连续区间上的'B'
均可以向前交换一次,交换次数为这段连续的'B'
的长度,而经过一次交换之后,由于每个下标只能被选择一次,那么仅有第一个'B'
还能向前交换,可以认为这段连续的'B'
交换完成后就只剩下开头这一个'B'
了。使用变量维护还能向前交换的'B'
的个数,从后往前遍历模拟即可。
代码:
#include <bits/stdc++.h>
using namespace std;
string s;
void solve() {
int n;
cin >> n >> s;
int cnt = 0, ans = 0;
for (int i = n - 1; i >= 0; i--) {
if (s[i] == 'B') {
cnt++;
} else {
ans += cnt;
cnt = min(cnt, 1);//如果当前没有遇到过B,就让cnt保持在0
}
}
cout << ans << endl;
}
int main() {
solve();
return 0;
}
C.Matching Arrays
题意:
给出两个包含 n n n个数字的数组 a a a和 b b b,这两个数组的美丽值为满足 a i > b i a_i > b_i ai>bi的下标 i i i的个数。
题目会给出一个数字 x x x,问能否对 b b b重新排列,使得这两个数组的美丽值等于 x x x。
分析:
虽然题目要求只能对 b b b重排,但为了便于处理,两个数组都需要排序,同时为了记录数组 a a a原本的数字位置,需使用结构体存储数据。
贪心:将 a a a中最大的 x x x个元素与 b b b中最小的 x x x个元素按大小次序进行比较,如果这部分元素无法构成 x x x的美丽值,由于 a a a中剩余元素更小, b b b中剩余元素更大,那么无论怎么交换元素,都无法使美丽值增加,此时本题无解。
检查:比较完 a a a中最大的 x x x个元素与 b b b中最小的 x x x个元素后,还需要考虑剩余的元素是否还会产生美丽值,同样采用按大小次序依次比较,如果产生美丽值那么同样表示本题无解。
输出:如果可以构造,那么需要根据记录的 a a a中每个数字排序前的位置将对应的 b b b数组元素输出。
代码:
#include <bits/stdc++.h>
using namespace std;
struct Node{
int val, id;
bool operator < (const Node &o) const {
return val < o.val;
}
}a[200005], b[200005];
int ans[200005];
void solve() {
int n, x;
cin >> n >> x;
for (int i = 1; i <= n; i++) {
cin >> a[i].val;
a[i].id = i;
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++) {
cin >> b[i].val;
b[i].id = i;
}
sort(b + 1, b + 1 + n);
for (int i = 1; i <= x; i++) {
if (a[i + n - x].val <= b[i].val) {//a中最大的x个与b中最小的x个对位比较
cout << "NO" << endl;
return;
}
ans[a[i + n - x].id] = b[i].val;//将当前b中元素放入对应的a中元素原本所在下标对应的位置上
}
for (int i = 1; i <= n - x; i++) {
if (a[i].val > b[i + x].val) {//剩余的n-x个元素对位比较
cout << "NO" << endl;
return;
}
ans[a[i].id] = b[i + x].val;
}
cout << "YES" << endl;
for (int i = 1; i <= n; i++) {
cout << ans[i] << ' ';
}
cout << endl;
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
D.Ones and Twos
题意:
给出一个仅包含 1 , 2 1,2 1,2的数组。
有 q q q个询问,询问分以下两种情况:
-
"1 s"
,询问数组中能否找出一个子段和为 s s s -
"2 i v"
,将数组中第 i i i个数字修改为 v v v
分析:
通过分析样例,可以发现以下规律:如果子段的左右端点数字均为1,那么可以组成任意值在 1 ∼ 1 \sim 1∼(子段数字之和)以内的数字。
根据以上规律,想要组成尽可能多的数字,那么选择的一定是最左和最右的两个 1 1 1中间的子段(包含端点)。
然后需要根据以下情况进行分类讨论:
-
数组中存在 1 1 1,这两个 1 1 1之间的子段总和为 s u m sum sum
-
x ≤ s u m x \le sum x≤sum,则可以组成
-
x > s u m x \gt sum x>sum,分成以下两种情况:
-
x x x与 s u m sum sum奇偶性相同,为了尽可能使总和最大,一定会选择将选择的子段向左右扩散,且此时左右元素一定均为2,只要整个数组的数字总和可以达到 x x x,那么就能组成 x x x。
-
奇偶性不同时,可以删去子段一侧的 1 1 1,再加上另一侧的 2 2 2,看组成的子段数字总和能否到达 x x x。
-
-
-
数组中不存在 1 1 1,那么能组成的只有偶数,且能组成的偶数 x x x的值要在数组中数字总和的范围内。
hint:可以通过 s e t set set对所有 1 1 1所在的位置(下标)进行维护,通过 ∗ ( b e g i n ( ) ) *(begin()) ∗(begin())和 ∗ ( − − e n d ( ) ) *(--end()) ∗(−−end())(end()函数返回的是最后一个元素的下一个迭代器,需要通过前自减得到最后一个元素的迭代器)来获得集合中最小和最大的元素。
代码:
#include <bits/stdc++.h>
using namespace std;
int n, q, a[100005], cnt;
set<int> S;
bool check(int x) {
if (S.empty()) {//数组中没有1
if (x % 2 == 1) return false;
if (n * 2 < x) return false;
return true;
}
int first = *S.begin(), last = *(--S.end());//获得最前和最后的1所在的下标
int sum = (last - first + 1) * 2 - S.size();//将区间内所有的数视为2,计算出总和,减去1的数量,就是该子段的数字总和
if (sum >= x) return true;//被1包围的子段已经能组成x了
if (x % 2 == sum % 2) {
int add = n - (last - first + 1);//计算出未被加上的2的数量
if (sum + add * 2 >= x) return true;
} else {
int add = max(n - last, first - 1);//计算左右两边最多有多少个2
if (sum - 1 + add * 2 >= x) return true;
}
return false;
}
void solve() {
S.clear();
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] == 1) {
S.insert(i);
cnt++;
}
}
while (q--) {
int op;
cin >> op;
if (op == 1) {
int x;
cin >> x;
if (check(x)) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
} else {
int i, v;
cin >> i >> v;
if (a[i] == 1) S.erase(i);
a[i] = v;
if (a[i] == 1) S.insert(i);
}
}
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
E.Permutation Sorting
更新中…
以下学习交流QQ群,群号: 546235402,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。