C o d e f o r c e s R o u n d 946 ( D i v . 3 ) \Huge{Codeforces~Round~946~(Div.3)} Codeforces Round 946 (Div.3)
题目链接:Codeforces Round 946 (Div. 3)
文章目录
- Problems A. Phone Desktop
- 题意
- 思路
- 标程
- Problems B. Symmetric Encoding
- 题意
- 思路
- 标程
- Problems C. Beautiful Triple Pairs
- 题意
- 思路
- 标程
- Problems D. Ingenuity-2
- 题意
- 思路
- 标程
- Problems E. Money Buys Happiness
- 题意
- 思路
- 标程
Problems A. Phone Desktop
题意
有无限个 3 × 5 3\times 5 3×5的二维方阵,然后给出x个 1 × 1 1 \times 1 1×1的方块和y个 2 × 2 2\times 2 2×2的方块,要求所有方块全部放在方阵里,求最少需要多少个方阵。
思路
每个方阵能放两个 2 × 2 2\times 2 2×2的方块,或者15个 1 × 1 1\times 1 1×1的方块,然后根据 2 × 2 2\times 2 2×2个数,求出至少需要多少个方阵,然后再根据 1 × 1 1\times 1 1×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 s,然后要求求解其加密前的字符串。
加密过程为:
- 先将 s s s去重排序得到 s 2 s2 s2。
- 然后对 s 2 s2 s2进行首尾映射:
- 最后根据映射改变 s s s。
思路
根据加密过程进行模拟。
字符串是否加密,其去重排序的结果是不变的,然后用 m a p map 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 n n的数列 a a a,如果任意三元组 j ( 1 ≤ j ≤ n − 2 ) [ a j , a j + 1 , a j + 2 ] j (1 \le j \le n - 2) [a_j, a_{j + 1}, a_{j + 2}] j(1≤j≤n−2)[aj,aj+1,aj+2]能够与其他三元组符合:任意一个位置不同其他两个位置相同,那么这个三元组就是美丽的,求其中美丽三元对的个数。
- 3 ≤ n ≤ 2 ⋅ 1 0 5 3 \le n \le 2 \cdot 10^5 3≤n≤2⋅105
思路
这道题的数据范围比较大,我们可以发现,对于任意三元组,如果我们需要找出所有三元组中有多少满足要求,其结果是:所有与它有至少两个相等的三元组个数,减去所有与它全部相等的三元组个数。
因此我们可以遍历一遍,然后用map存一下所有三元组中的两个,一共需要用三个map,然后再存一下每个三元组本身的个数,然后遍历一遍求解即可。
需要注意的是,我们求解过程中每个美丽三元组都加了两次,所以需要最后结果除以 2 2 2。
标程
#define int long long
#define PII pair<int, int>
#define fi first
#define se secondvoid 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 R R,然后给出一串操作,要求每次选择直升机移动,或者漫游车移动,要求最后到达同意位置,题目要求输出一种具体解决方案,并且直升机或者漫游车都至少移动一次。
思路
本题是一道模拟,我们考虑如下情况:一般地,如果讲所有操作全用一种机器移动,设最后坐标为 ( x , y ) (x,y) (x,y),若 x x x或 y y y为奇数,则题目无解,否则可以将两个机器都移动到 ( x 2 , y 2 ) (\frac{x}{2},\frac{y}{2}) (2x,2y)位置。
实现方法就是:每个机器都移动每种操作数的一半。
唯一需要注意的就是:
- 不存在解决方案的情况:除了上述的横纵坐标出现奇数,还有就是当 n n n为 2 2 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 k k,每个月有购买幸福的机会,但是本月工资不能用来购买本月幸福,工资可以累计。每个月的幸福有价值和幸福度,作者希望 n n n个月过后自己的幸福度最多,求幸福度最多为多少?
数据范围:
- 0 ≤ c i ≤ 1 0 8 , 1 ≤ h i ≤ 1 0 3 0 \le c_i \le 10^8,1 \le h_i \le 10^3 0≤ci≤108,1≤hi≤103, c i c_i ci表示幸福价值, h i h_i hi表示幸福度。
- ∑ i h i ≤ 1 0 5 \sum_i h_i\le 10^5 ∑ihi≤105
- 1 ≤ n ≤ 50 , 1 ≤ k ≤ 1 0 8 1 \le n \le 50, 1 \le k \le 10^8 1≤n≤50,1≤k≤108
思路
跟据题目来看,这道题应该是一道 d p dp dp,但是状态转移方程不太好推。
容易注意到幸福度之和比较小,同时 n n n的数据范围也比较小,并且 O ( n × ∑ i h i ) O(n \times \sum_i h_i) O(n×∑ihi)的时间复杂度也不会超时。
通过观察发现,当幸福度最多时,满足幸福度最多,所用价值最少。因此我们考虑讲幸福度看作背包问题中的价值,幸福价值看作重量,然后把问题转化为求解幸福度最多,并且所用价值最少的情况。那么这道题就转化为了一道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);//数据范围会爆intf[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;
}