Codeforces Round 972(Div.2)A+B+C
昨天晚上做了Codeforces Round 972(Div.2)的A、B两道题,今天补一下思路。
题目来源:https://codeforces.com/contest/2005
A. Simple Palindrome
题目描述
输入输出样例及解释
思路
说穿了这道题目的意思就是使用aeiou
五个字母来构造给定长度n
的字符串,使得字符串中所包含的回文子串最少。
显然使aeiou
中所有相同的字母排列在一起,可以使得最终生成字符串中所包含的回文子串最少,比如当n=6
的情况下,aaeiou
是最优解,而当n=10
的情况下,aaeeiioouu
是最优解。显然,当n<5
时,比如n=4
时,不出现重复的字符是最优解,比如aeio
。
因此现在的问题转换为,如何快速地在给定n
的情况下按要求输出字符。这一部分非常偏向于工程,从codeforces的官方题解中学习到了很多,在这里做一下记录。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
const string VOWELS = "aeiou";
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t --) {
int n;
cin >> n;
vector<int> v(5, n/5); // 使用v来记录字符输出的次数, 初始化为n/5
for(int i=0; i<n%5; i++) { // 以19为例, 19/5 = 3, 余数为4, 余数归到a, e, i, o四个字符当中
v[i] ++; // 使得最终的输出为aaaaeeeeiiiioooouuu共19位
}
for(int i=0; i<5; i++){ // 遍历五个字符
for(int j=0; j<v[i]; j++) { // 按次数输出对应字符
cout << VOWELS[i];
}
}
cout << endl;
}
return 0;
}
B1. The Strict Teacher (Easy Version)
题目描述
输入输出样例及解释
思路
这道题是B问题的简单版本,题目描述中明确有:m=2, q=1
,意味着只有两个老师,并且目标只会处于一个位置。这样就使得问题分成了三种情况,分别是目标在两个老师左侧/右侧/中间。
对于前两种情况,最优解只可能是目标向着两边运动,而老师也同时向两边运动,答案为距离边界最近的老师与边界的距离。
而对于第三种情况,最优解只可能是目标在原地不动,因为无论它向哪个方向运动只会减小自己与老师的距离,因此这种情况的最终答案是老师之间的距离除以二。
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while(t --){
int l, m, q;
cin >> l >> m >> q;
int x1, x2, y;
cin >> x1 >> x2;
if(x1 > x2) swap(x1, x2);
while(q --){
cin >> y;
if (x1 <= y && y <= x2){
cout << (x2 - x1) / 2 << endl;
}
else if (y < x1){
cout << x1 - 1 << endl;
}
else {
cout << l - x2 << endl;
}
}
}
return 0;
}
B2. The Strict Teacher (Hard Version)
这道题的题目描述和输入输出样例和Easy Version是相同的,区别在于B2当中的m和q是不固定的,相当于可能存在多于两个老师来追捕目标,而目标的位置也是不固定的,需要根据输入的q来具体分析。
思路
尽管m和q是不固定的,但是解决问题的思路是类似的,仍然分为三种情况,即目标在老师的左侧、右侧和中间。前两种情况与B1相同,而第三种情况略有不同,因为老师的数目可能是大于2的。因此显然要找到将目标位置夹在中间的距离目标最近的两个老师。
这里需要先对老师的位置排个序,再使用二分查找来找到不大于目标位置的最后一个位置a和不小于目标位置的第一个位置b,该情况下的最终答案即为 ( b − a ) / 2 (b - a) / 2 (b−a)/2。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
int t;
int n, m, q;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> t;
while(t--) {
cin >> n >> m >> q;
vector<int> p(m);
for(int i=0; i<m; i++){
cin >> p[i];
}
sort(p.begin(), p.end()); // 对教师的位置进行排序从而进行二分查找
while(q --) {
int x;
cin >> x;
int v = upper_bound(p.begin(), p.end(), x) - p.begin(); // 使用upper_bound查找不小于q的第一个位置,
// 如果查找失败则返回迭代器尾部
if(v == 0) cout << p[0] - 1 << endl;
else if (v == m) cout << n - p[m-1] << endl;
else cout << (p[v] - p[v-1]) / 2 << endl;
}
}
return 0;
}
此处顺便对C++当中的upper_bound
用法进行了练习,使用upper_bound
可以快速地查找有序数组p中不小于x的第一个数在可迭代对象当中的位置。
本文想对C题也进行一下补题,但是看到C题涉及到动态规划的部分,这一部分我还没有复习到,暂时先放弃了。