20级训练赛Round#4题解
前言
诶,大部分人好像都没补题的习惯,是因为太难了还是没有题解呀?不过还是希望大家认真补题,如果碰到不会的不去弄懂那下次还是不会的。
因为我太懒了,所以每次训练赛都没有准备题解,之后题解我会尽量做,如果没有的话希望大家自己能主动去网上找题解,积极主动!相信过了这么久,其实大家也已经进步许多了,一定要坚持下去,不要松懈,你们就是江理 A C M ACM ACM的希望!
A.凯少的动作序列
-
解题思路
按照贪心思想,我们的目标自然就是替换尽可能多的RU和UR对。 所以,当为RU和UR对的时候我们及时替换即可,这种策略自然是全局最优的。我们这里列举证明: R U R RUR RUR,最多一种,我们及时替换 R U RU RU,对于 R U RU RU之后的序列,我们并不会影响,而对于替换 R U RU RU之前的序列,由于已经被遍历过,前面即是最优解了,所以这种策略是合理的。
-
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n;
char s[N];
void solve(){
int ans = n;//什么都不替换自然为n。
for(int i = 1; i < n; ++ i){
if((s[i] == 'R' && s[i + 1] == 'U' || s[i] == 'U' && s[i + 1] == 'R')){
ans -- ;
i ++;
}
}
printf("%d\n",ans);
}
int main(){
while(scanf("%d", &n) != EOF){
scanf("%s", s + 1);
solve();
}
return 0;
}
B.凯少的秘密消息
-
解题思路
首先我们需要记录每个字符串的序号(因为我们需要通过字符串得到其序号),这很容易想到用 m a p map map记录。 那么对于分组,我们用 g r o u p group group来存储,其中每组我们按花费来排序,那么最优的替换选择就是同组内的第一个元素。 我们自然也需要记录字符串在那个组,这样才能知道字符串需要替换成哪个,所以我们需要用 i d x idx idx数组记录字符串所在的组别。这样,题目自然解决了,对于输入的 m m m个的单词,索引它所在的组,获取其组中的第一个字符串即可。累加最小花费。
-
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
//自然是想着将同意义的单词分组,然后排序取最低费用即可。
int n,k,m;
map<string,int> p;
int cost[N],idx[N];//idx[i]表示第i个单词所在的组序号。
string s;
vector<int> group[N];
//重写比较方法,根据花费来排序。
bool cmp(int i,int j){
return cost[i] < cost[j];
}
void solve(){
for(int i = 1; i <= k; ++ i){
sort(group[i].begin(),group[i].end(),cmp);
}
for(int i = 1; i <= k; ++ i){
/*
cout << i << " group" << endl;
for(auto &x:group[i]){
cout << x << " " << cost[x] << endl;
}
*/
}
long long ans = 0;
while(m -- ){
cin >> s;
//获取序号。
int pos = p[s];
//根据组号选择最小值。
//cout << s << " " << idx[pos] << endl;
ans += cost[group[idx[pos]][0]];
}
cout << ans << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin >> n >> k >> m;
for(int i = 1; i <= n; ++ i){
cin >> s;
p[s] = i;//记录其所在下标。
}
for(int i = 1; i <= n; ++ i){
cin >> cost[i];
}
int cnt,x;
for(int i = 1; i <= k; ++ i){
cin >> cnt;
while(cnt --){
cin >> x;
group[i].push_back(x);
//记录x所在的组。
idx[x] = i;
}
}
solve();
return 0;
}
C.尚佬的投篮得分
-
解题思路
双指针维护一个长度为 k k k的区间。 处理其中的概率为 0 0 0的点,注意区间移动的时候要剔除掉过期元素。
-
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,k;
int a[N],p[N],ans;//ans统计得分。
void solve(){
//此题易知用双指针获取最大值,注意,我们需要时刻更新我们能用的次数。
int l = 1,r = 1;
int maxx = 0;
while(r <= n){
while(r <= n && r - l < k){
if(!p[r]){
ans += a[r];
}
r ++ ;
}
maxx = max(ans,maxx);//保存最大值。
if(!p[l]){
ans -= a[l];
}
l ++;
}
printf("%d\n",maxx);
}
int main(){
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; ++ i){
scanf("%d", &p[i]);
if(p[i]){
ans += a[i];
}
}
solve();
return 0;
}
D.以旧换新
-
解题思路
对于 a , b a,b a,b,由于题目中一定有解,所以 a < = b a<=b a<=b。我们有几种讨论的情况,当 a a a的位数小于 b b b的位数的时候,不管 a a a怎么变化,始终小于 b b b,所以我们自然是将 a a a的位数排序构造即可;当 a = b a=b a=b,此时最大则是 b b b,直接输出即可;而当 a < b a<b a<b,此时 a a a受 b b b的限制,我们构建 a a a的时候需要考虑 b b b,由于存在未知情况,所以我们需要用 d f s dfs dfs去搜索,注意贪心搜索和剪枝 。那么何为贪心搜索,在构造 a a a的时候,我们自然想往 b b b上靠,如果我们目前不知道构建的 a a a是否大于等于 b b b,那么我们自然只会进行两种搜索策略,一种是当前位等于 b b b对应的位,这种即是持续往 b b b相等那里靠,另一种则是当前位小于 b b b对应的位(从可选择的选择最大值,其余的跳过),一但构建这种,那么构建的数一定小于 b b b。那么如果我们已知构建的 a a a小于 b b b,那么我们只会有一种搜索策略,就是构建位数上的可选最大值。有了这两种思想,按照优先顺序列写,我们最先构建出来的数一定是最大值。注意利用一个全局变量控制 d f s dfs dfs的进行,即代表是否找到最优解。此题特别具有代表性,希望大家好好整理,当然,方法不只一种,有其他方法也可以去学习讨论。
-
参考代码
#include<bits/stdc++.h>
using namespace std;
string a,b;//利用字符串存储更方便。
int cnt[10];
bool ok;//判断是否找到最优解。
void dfs(long long ans = 0,int len = 0,bool flag = false){
//flag代表当前路径构造的ans是否已经知晓其小于0了。
//经过我们的过滤,实际上我们已经得到了对应的结果。
if(len == a.size()){
ok = true;
cout << ans << endl;
return;
}
for(int i = 9; i >= 0; -- i){
if(ok)return;
if(cnt[i]){
if(flag){
//填充最大的。
cnt[i] --;
dfs(ans * 10 + i,len + 1,flag);
cnt[i] ++;
break;
}
else{
if(i == b[len] - '0'){
cnt[i] --;
dfs(ans * 10 + i,len + 1,flag);
cnt[i] ++;
}
else if(i < b[len] - '0'){
cnt[i] --;
dfs(ans * 10 + i,len + 1,true);
cnt[i] ++;
}
}
}
}
}
void solve(){
if(a.size() < b.size()){
//说明a不依赖于b,我们输出所能构建的最大a。
sort(a.begin(),a.end(),greater<char>() );
cout << a << endl;
}
else if(a == b){
cout << a << endl;
}
else{
for(auto &x : a){
cnt[x - '0'] ++;
}
dfs();
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin >> a >> b;
solve();
return 0;
}
E.交换相邻元素
-
解题思路
贪心,题目实际上告诉我们只能将连续1的段 [ l , r ] [l,r] [l,r],使得 [ l , r + 1 ] [l,r + 1] [l,r+1]变成有序的 ,所以我们对这些连续1的段所控制的区间排序即可。
最后判断是否是有序的即可。
-
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
//
int n,a[N];
char s[N];
void solve(){
for(int i = 1; i <= n - 1; ++ i){
if(s[i] == '1'){
int j = i;
while(j + 1 <= n - 1 && s[j + 1] == '1'){
j ++ ;
}
sort(a + i,a + 1 + j + 1);
i = j;//注意这里需要直接跳到j。
}
}
for(int i = 1; i <= n; ++ i){
if(a[i] != i){
puts("NO");
return;
}
}
puts("YES");
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
}
scanf("%s",s + 1);
solve();
return 0;
}
F.帮帮凯少保护他的羊
-
解题思路
我们的策略自然是堵住所有的狼,让它无法吃掉羊。那么只有一种情况我们是无法保护羊的,即羊在狼的周围。所以我们只需要判断是否存在这种情况即可。如果不存在,那么直接在所有的空地放狼。
-
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500 + 5;
int n,m;//n * m的矩阵。
char s[N][N];
bool check(int x,int y){
if((x > 0 && s[x - 1][y] == 'S') || (y > 0 && s[x][y - 1] == 'S') ||
(x < n - 1 && s[x + 1][y] == 'S') || (y < m - 1 && s[x][y + 1] == 'S')){
return false;
}
return true;
}
void solve(){
for(int i = 0; i < n; ++ i){
for(int j = 0; j < m; ++ j){
if(s[i][j] == 'W'){
if(!check(i,j)){
puts("NO");
return;
}
}
}
}
puts("YES");
for(int i = 0; i < n; ++ i){
for(int j = 0; j < m; ++ j){
if(s[i][j] == '.'){
s[i][j] = 'D';
}
}
puts(s[i]);
}
}
int main(){
scanf("%d %d", &n, &m);
for(int i = 0; i < n; ++ i){
scanf("%s",s[i]);
}
solve();
return 0;
}

浙公网安备 33010602011771号