分享昨天晚上Codeforces一场比赛的题解
A New Palindrome
#题意
给定一个由小写字母组成的回文串, 你需要判断是否可以重新排列字母,使之成为与原字符串不同的新回文串。
#分析
既然是回文串,那么左右两边是对称的,只需考虑一边即可。 重新排列字母,且与原来字符串不同,显然相同字母交换顺序是没有意义的, 那只需要找出回文串的一边包含多少个不同字母,大于1即可。
#代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 60;
char letters[N];
void solve(){
string str;
cin >> str;
int sz = str.length();
int cnt = 0;
memset(letters, '\0', sizeof(letters));
for(int i = 0; i < sz / 2; ++i){
letters[str[i] - 'a']++;
}
for(int i = 0; i <= 25; ++i) {
if(letters[i] != 0)cnt++;
}
if(cnt > 1)cout << "YES" << "\n";
else cout << "NO" << "\n";
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _; cin >> _;
while(_ --){
solve();
}
return 0;
}
B Maximum Sum
#题意
给定一个数组,每个元素都不同。你被规定进行
k次操作,操作如下:1.选出数组中最小的两个数,将它们删除
2.选出数组中最大的一个数,将其删除
每次操作只能选择其中一种,求出剩余数的最大和。
#分析
常规的想法应该是贪心,每一次将最小两个数的和与最大数比大小,然后删去较小值。 但是这样无法求出最优解,我们需要换一种思路。
我们可以将操作次数枚举:指定需要进行
k次操作,其中包含i次操作1,那么操作2即为k-i次。 通过枚举我们可以知道每一次操作后的结果,用一个前缀和来维护,接着找出最大值即可。
#代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 9;
int nums[N];
int presum[N];
void solve(){
int n, k; cin >> n >> k;
int sum = 0;
for(int i = 1; i <= n; ++i){
cin >> nums[i];
}
sort(nums + 1, nums + 1 + n);
for(int i = 1; i <= n; ++i){
presum[i] = presum[i - 1] + nums[i];
}
int ans = 0;
for(int i = 0; i <= k; ++i){//枚举操作1
int l = 2 * i;
int r = n - (k - i);
ans = max(ans, presum[r] - presum[l]);
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _; cin >> _;
while(_--){
solve();
}
return 0;
}
C Contrast Value
#题意
给定一个a数组,定义一个值contract为,通过a数组得到一个b数组,满足以下条件:
1.b非空,至少有一个元素
2.b是a的一个子序列
3.b的contract值与a相等
需要求得b序列的最小长度。
#分析
通过式子我们可以发现,当一串数值是单调的,这串数值的contract值可以直接由头尾元素算得,中间元素不影响大小。例如1、3、6、8算出的值和1、8的效果是一样的。
这样我们只需要找到改变单调性的节点即峰值点,再算上首尾节点即可。
#代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 9;
int a[N], p[N];
void solve(){
int n; cin >> n;
for(int i = 1; i <= n; ++i){
cin >> a[i];
}
int ans = 1;
//首先计入最后一个点
//若序列单调,则中间值可以省略
int tag = -1;
//表示单调增->1或单调减->0
//一开始不知道单调性,设为-1
for(int i = 2; i <= n; ++i){
if(a[i] == a[i - 1])continue;
//当不确定单调性时,不知道第一个点从何取起
//若第二个数和第一个数相等,则不作为转折点(不影响起点)
//若不相等,第一个数即为起点,得ans++
if(tag == -1 && a[i] != a[i - 1])ans++;
if(tag == 0 && a[i] > a[i - 1])ans++;
if(tag == 1 && a[i] < a[i - 1])ans++;
tag = a[i] > a[i - 1] ? 1 : 0;
}
cout << ans << '\n';
}
signed main(){
int _; cin >> _;
while(_ --){
solve();
}
return 0;
}