Codeforces Round 954(Div. 3)
目录\(C\). UpdateQueries
-
对索引数组 \(ind\) 去重排序
-
对字符串 \(c\) 排序
顺序遍历索引数组,将 \(s[ind[i]]\) 替换成 \(c[i]\)
void solve() {
int n, m; cin >> n >> m;
string s; cin >> s;
vector<int> idx(m);
string cset;
for(int i = 0; i < m; i++) cin >> idx[i];
cin >> cset;
sort(idx.begin(), idx.end());
idx.erase(unique(idx.begin(), idx.end()), idx.end());
sort(cset.begin(), cset.end());
int len = idx.size();
for(int i = 0; i < len; i++){
s[idx[i] - 1] = cset[i];
}
cout << s << endl;
}
\(D\). Mathematical Problem
本题思路来源于xrenena
贪心一手
当 \(n = 2\) 时,直接将这个二位数转化成数字输出即可
当 \(n > 2\) 且数字中存在 \(0\) 时 ,直接输出 \(0\) (除了 \(n = 3\) 的情况)
-
这是因为可以直接乘 \(0\)
-
当 \(n == 3\) 时,要特判一手,如果字符串两侧出现了 \(0\) 则直接输出 \(0\) 后
return
即可,如果两侧没有出现 \(0\) (中间出现 \(0\) 不用管),则直接交给下面的逻辑判断即可(例如"091"、"910"
当 \(n>2\) 且无法直接乘 \(0\) 时:
- 因为只能插入 \(n - 2\)个符号,所以必然会出现一个二位数,我们枚举这个二位数出现的位置
- 将划分好的每个数字插入一个数组,遍历划分好的该数组
- 遇到 \(v[i] = 1\) 时直接跳过,其他的全加起来
- 最后判断,如果 \(sum = 0\),直接输出 \(1\)(因为这种情况可能是全是 \(1\) ,例如
"101"
被划分成1
、01
) - 如果 \(sum > 0\),直接输出 \(sum\) 即可
//如果数字中 n > 2 且 数字中存在0,直接输出0,对0乘法
//(但是要特别判断n = 3的情况,例如901,但是3位数中两边为0的时候也可以直接输出0)
//枚举每个2位数的位置,用一个v数组把分好的数字存起来
//遍历v数组,遇到1就跳过它,因为1用来做乘法的
void solve() {
int n; cin >> n;
string s; cin >> s;
if(s.find('0') != -1 && n > 2){
if(n == 3){//特判3位数中出现0在两侧的情况(091,910的情况)
if(s[0] == '0' || s[2] == '0'){
cout << 0 << endl;
return;
}
}else{
cout << 0 << endl;
return;
}
}
if(n == 2){
cout << (s[0] - '0') * 10 + (s[1] - '0') << endl;
return;
}
int res = 0x3f3f3f3f;
//枚举二位数在哪个位置
for(int idx = 0; idx < s.size() - 1; idx++){
//v存放分好的数字
vector<int> v;
for(int i = 0; i < s.size(); i++){
if(i == idx){//到了二位数的位置,直接将二位数放进v
v.push_back((s[i] - '0') * 10 + (s[i + 1] - '0'));
i++;
continue;
}
v.push_back(s[i] - '0');
}
int sum = 0;
//遍历加起来
for(auto i : v){
if(i == 1) continue;
sum += i;
}
if(sum == 0) {//如果sum是0可以直接输出1,因为所有1都被跳过了
res = min(1, res);
}
else res = min(sum, res);
}
cout << res << endl;
}
\(E\). Beautiful Array
方法一:贪心+滑动窗口
先判断是否能使数组美观:
- 通过一个\(map\) 记录取模 \(k\) 的结果相同的数的个数(例如 \(3 \% 2\) 和 \(5 \% 2\) 的结果相同)
- 遍历 \(map\),记录余数相同的组中,元素个数为奇数的组有多少个
- 如果个数为奇数的组 > 1,则无论如何都无法转化为美丽数组,则输出 \(-1\),直接
return
否则可以转化成美丽数组:
-
按照余数排序一下
-
滑动窗口维护每个余数相同的组,用一个 \(v\) 数组将组内元素全装进去排个序(这样就能保证最优选择一定是相邻两个数字进行匹配)
-
利用 \(sum\) 求两两配对的的前缀和,
- 如下 \(s[1]\) 表示 \((v[1] - v[0]) / k\)
- \(s[3]\) 表示 \((v[3] - v[2])\ /\ k + s[3 - 2]\)
- \(s[2]\) 表示 \((v[2] - v[1]) / k\)
- 这样我们枚举不用的点,用前缀和求代价,取最小值即可
#define int long long
//自定义排序
bool compare(int x, int y, int k) {
return (x % k) < (y % k);
}
void solve() {
long n, k; cin >> n >> k;
map<int, int> hash;
vector<int> a(n);
for(int i = 0; i < n; i++) {
cin >> a[i];
hash[a[i] % k]++;
}
int num = 0, cnt = 0;
//算一算余数相同的组中,元素个数为奇数的组有多少个
for(auto [k, v] : hash){
if(v % 2) cnt++, num = k;
}
//如果元素个数为奇数的组大于1个,则无论如何都不能变成完美数组
if(cnt > 1){
cout << -1 << endl;
return;
}
//按照余数排序
sort(a.begin(), a.end(), [&](int x, int y){
return compare(x, y, k);
});
int res = 0;
//滑动窗口维护每个余数相同的组
int i = 0, j = 1;
while(j < n){
//将余数相同的数全放进v数组
vector<int> v;
v.push_back(a[i]);
while(j < n && a[i] % k == a[j] % k){
v.push_back(a[j]);
j++;
}
//对v中排序
sort(v.begin(), v.end());
//算出每隔位的前缀和
int len = v.size();
vector<int> sum(len);
for(int u = 1; u < len; u++){
if(u - 2 >= 0) sum[u] = sum[u - 2];
sum[u] += (v[u] - v[u - 1]) / k;
}
//如果v中的个数为偶数,直接前后两两配对即可
if(len % 2 == 0) res += sum[len - 1];
else {//如果是奇数则枚举哪个点用不上
int ans = 0x3f3f3f3f;//记录最小代价
for(int u = 0; u < len; u += 2){
int all = 0;
if(u > 0) all += sum[u - 1];
if(u < len - 1) all += sum[len - 1] - sum[u];
ans = min(ans, all);
}
res += ans;
}
i = j;
j++;
}
cout << res << endl;
}
方法二:DP
该方法来源于yuzhongrun
详细方法题解可跳转至div3E. Beautiful Array
// https://codeforces.com/contest/1986/problem/E
#include <bits/stdc++.h>
#define caseT \
int T; \
cin >> T; \
while (T--)
using namespace std;
using ll = long long;
void solve() {
ll n, k, ans = 0, num2 = 0;
cin >> n >> k;
vector<ll> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
sort(nums.begin(), nums.end());
map<ll, vector<ll>> H;
for (ll num : nums) H[num % k].push_back(num);
for (auto &[key, p] : H) {
if (p.size() & 1 && ++num2 >= 2) {
cout << "-1" << endl;
return;
}
int len = p.size();
vector<vector<ll>> dp(len, vector<ll>(2));
dp[0][0] = INT_MAX;
if (len > 1) dp[1] = {(p[1] - p[0]) / k, INT_MAX};
for (int i = 2; i < len; i++) {
ll t = (p[i] - p[i - 1]) / k;
if (i & 1) {
dp[i][0] = min(dp[i - 2][0], dp[i - 1][1]) + t;
} else {
dp[i][1] = dp[i - 1][0];
dp[i][0] = min(dp[i - 2][0], dp[i - 2][1]) + t;
}
}
ans += (len & 1) ? min(dp[len - 1][1], dp[len - 1][0]) : dp[len - 1][0];
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
caseT solve();
return 0;
}
标签:idx,int,sum,954,len,Codeforces,++,Div,dp
From: https://www.cnblogs.com/xiwen-ydys/p/18293747