Codeforces Round 946 (Div
\(\Huge{Codeforces~Round~946~(Div.3)}\)
题目链接:Codeforces Round 946 (Div. 3)
Problems A. Phone Desktop
题意
有无限个\(3\times 5\)的二维方阵,然后给出x个\(1 \times 1\)的方块和y个\(2\times 2\)的方块,要求所有方块全部放在方阵里,求最少需要多少个方阵。
思路
每个方阵能放两个\(2\times 2\)的方块,或者15个\(1\times 1\)的方块,然后根据\(2\times 2\)个数,求出至少需要多少个方阵,然后再根据\(1\times 1\)方块个数判断需不需要加方阵。
标程
void Solved() {
int x, y; cin >> x >> y;
int sum1 = (y + 1) / 2;
int sum2 = sum1 * 7;
if(y & 1) sum2 += 4;
sum2 -= x;
if(sum2 < 0) {
sum2 *= -1;
sum1 += (sum2 + 14) / 15;
}
cout << sum1 << endl;
}
Problems B. Symmetric Encoding
题意
题目给出一个字符串\(s\),然后要求求解其加密前的字符串。
加密过程为:
- 先将\(s\)去重排序得到\(s2\)。
- 然后对\(s2\)进行首尾映射:

- 最后根据映射改变\(s\)。
思路
根据加密过程进行模拟。
字符串是否加密,其去重排序的结果是不变的,然后用\(map\)记录字母映射,然后对加密字符串解密即可。
标程
void Solved() {
int n; cin >> n;
string s; cin >> s;
string ss = s, s1;
sort(ss.begin(), ss.end());
s1 = s1 + ss[0];
for(int i = 1; i < n; i ++ ) {
if(s1.back() != ss[i]) s1 = s1 + ss[i];
}
map<char, char> mp;
int len = s1.size();
for(int i = 0; i < len; i ++ ) {
mp[s1[i]] = s1[len - i - 1];
mp[s1[len - 1 - i]] = s1[i];
}
for(int i = 0; i < n; i ++ ) {
cout << mp[s[i]];
} cout << endl;
}
Problems C. Beautiful Triple Pairs
题意
题目给出一串长度为\(n\)的数列\(a\),如果任意三元组\(j (1 \le j \le n - 2) [a_j, a_{j + 1}, a_{j + 2}]\)能够与其他三元组符合:任意一个位置不同其他两个位置相同,那么这个三元组就是美丽的,求其中美丽三元对的个数。
- \(3 \le n \le 2 \cdot 10^5\)
思路
这道题的数据范围比较大,我们可以发现,对于任意三元组,如果我们需要找出所有三元组中有多少满足要求,其结果是:所有与它有至少两个相等的三元组个数,减去所有与它全部相等的三元组个数。
因此我们可以遍历一遍,然后用map存一下所有三元组中的两个,一共需要用三个map,然后再存一下每个三元组本身的个数,然后遍历一遍求解即可。
需要注意的是,我们求解过程中每个美丽三元组都加了两次,所以需要最后结果除以\(2\)。
标程
#define int long long
#define PII pair<int, int>
#define fi first
#define se second
void Solved() {
int n; cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
}
map<PII, int> mp1, mp2, mp3;
map<tuple<int, int, int>, int> mp;
for(int i = 1; i <= n - 2; i ++ ) {
mp[{a[i], a[i + 1], a[i + 2]}] ++;
mp1[{a[i + 1], a[i + 2]}] ++;
mp2[{a[i], a[i + 2]}] ++;
mp3[{a[i], a[i + 1]}] ++;
}
int res = 0;
for(int i = 1; i <= n - 2; i ++ ) {
res += mp1[{a[i + 1], a[i + 2]}] - mp[{a[i], a[i + 1], a[i + 2]}];
res += mp2[{a[i], a[i + 2]}] - mp[{a[i], a[i + 1], a[i + 2]}];
res += mp3[{a[i], a[i + 1]}] - mp[{a[i], a[i + 1], a[i + 2]}];
}
cout << res / 2 << endl;
}
Problems D. Ingenuity-2
题意
题目给出上下左右四种操作,分别用“WESN"对应。现在有一个直升机H和漫游车\(R\),然后给出一串操作,要求每次选择直升机移动,或者漫游车移动,要求最后到达同意位置,题目要求输出一种具体解决方案,并且直升机或者漫游车都至少移动一次。
思路
本题是一道模拟,我们考虑如下情况:一般地,如果讲所有操作全用一种机器移动,设最后坐标为\((x,y)\),若\(x\)或\(y\)为奇数,则题目无解,否则可以将两个机器都移动到\((\frac{x}{2},\frac{y}{2})\)位置。
实现方法就是:每个机器都移动每种操作数的一半。
唯一需要注意的就是:
- 不存在解决方案的情况:除了上述的横纵坐标出现奇数,还有就是当\(n\)为\(2\),且两个操作不一样的情况。
- 当上下左右各出现一次的时候,需要特判,因为题目要求每个机器都至少移动一次。
标程
void Solved() {
int n; cin >> n;
string s; cin >> s;
int x = 0, y = 0;
map<char, int> mp;
for(int i = 0; i < n; i ++ ) {
if(s[i] == 'N') y ++, mp['N'] ++;
if(s[i] == 'S') y --, mp['S'] ++;
if(s[i] == 'W') x --, mp['W'] ++;
if(s[i] == 'E') x ++, mp['E'] ++;
}
if((x & 1) || (y & 1) || (n == 2 && s[0] != s[1])) {
cout << "NO\n"; return;
}
string ss = "WESN";
int t = 0;
for(auto i : ss) if(mp[i]) t ++;
if(t == 4 && n == 4) {//判断EWSN的情况
for(int i = 0; i < 4; i ++ ) {
if(s[i] == 'W' || s[i] == 'E') cout << 'H';
if(s[i] == 'S' || s[i] == 'N') cout << 'R';
}
cout << endl;
return;
}
map<char, int> mp2;
for(int i = 0; i < n; i ++ ) {
if(mp2[s[i]] < mp[s[i]] / 2) cout << 'R', mp2[s[i]]++;
else cout << 'H';
}
cout << endl;
}
Problems E. Money Buys Happiness
题意
作者每个月的工资为\(k\),每个月有购买幸福的机会,但是本月工资不能用来购买本月幸福,工资可以累计。每个月的幸福有价值和幸福度,作者希望\(n\)个月过后自己的幸福度最多,求幸福度最多为多少?
数据范围:
- \(0 \le c_i \le 10^8,1 \le h_i \le 10^3\),\(c_i\)表示幸福价值,\(h_i\)表示幸福度。
- \(\sum_i h_i\le 10^5\)
- \(1 \le n \le 50, 1 \le k \le 10^8\)
思路
跟据题目来看,这道题应该是一道\(dp\),但是状态转移方程不太好推。
容易注意到幸福度之和比较小,同时\(n\)的数据范围也比较小,并且\(O(n \times \sum_i h_i)\)的时间复杂度也不会超时。
通过观察发现,当幸福度最多时,满足幸福度最多,所用价值最少。因此我们考虑讲幸福度看作背包问题中的价值,幸福价值看作重量,然后把问题转化为求解幸福度最多,并且所用价值最少的情况。那么这道题就转化为了一道01背包问题。
标程
#define int long long
const int INF = 0x7fffffff;
void Solved() {
int n, k; cin >> n >> k;
vector<PII> a(n + 1);
int sum = 0;
for(int i = 1; i <= n; i ++ ) {
int x, y; cin >> x >> y;
a[i] = {x, y};
sum += y;
}
int res = 0;
vector<int> f(sum + 1, INF * 10000);//数据范围会爆int
f[0] = 0;
for(int i = 1; i <= n; i ++ ) {
for(int j = sum - a[i].se; j >= 0; j -- ) {
//当前工资需要满足能够买本月幸福和枚举的幸福,否则无法转移
if((i - 1) * k >= a[i].fi + f[j]) {
f[j + a[i].se] = min(f[j + a[i].se], f[j] + a[i].fi);
}
}
}
for(int i = sum; i; i -- ) {
if(f[i] != INF * 10000) {
res = i; break;
}
}
cout << res << endl;
}

浙公网安备 33010602011771号