前言
题解
2023 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(省赛) 。
第3题是真的有意思,差点想当数学题做了。
第5题出得也好,非常思维的一道题。
RC-u1 亚运奖牌榜
分值: 10分
题型; 模拟
这边有个技巧,vector天然支持大小比较。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> cards(2, vector<int>(3));
for (int i = 0; i < n; i++) {
int c, p;
cin >> c >> p;
cards[c][p - 1]++;
}
cout << cards[0][0] << " " << cards[0][1] << " " << cards[0][2] << "\n";
cout << cards[1][0] << " " << cards[1][1] << " " << cards[1][2] << "\n";
// 向量直接比较
if (cards[0] > cards[1]) {
cout << "The first win!\n";
} else {
cout << "The second win!\n";
}
return 0;
}
RC-u2 出院
分值: 15分
考察: stl容器的使用,模拟
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
map<string, string> hp;
for (int i = 0; i < n; i++) {
string name, tag;
cin >> name >> tag;
hp[name] = tag;
}
for (int i = 0; i < m; i++) {
string s;
cin >> s;
int len = s.size();
if (hp.count(s) > 0) {
cout << hp[s] << "\n";
continue;
}
string ans = "";
int hits = 0;
for (int j = 0; j < len - 1; j++) {
string k1 = s.substr(0, j + 1);
string k2 = s.substr(j + 1);
if (hp.count(k1) > 0 && hp.count(k2) > 0) {
ans = hp[k1] + hp[k2];
hits++;
}
}
if (hits == 1) {
cout << ans << '\n';
} else {
cout << "D\n";
}
}
return 0;
}
RC-u3 骰子游戏
分值: 20分
题型: 二进制枚举 + 暴力枚举 + 模拟
这题的码量其实特别大
- 编写光骰子获胜等级判定
- 二进制枚举,用于枚举需要变动的个数
- 确定重置的骰子后,暴力枚举可能的结果
胜率提升,需要保证改变后等级大于之前
如果能理顺这边的逻辑,那恭喜你,这题基本搞定。
总的时间复杂度为 O(25∗65∗6)O(2^5 * 6^5 * 6)O(25∗65∗6)。
#include <bits/stdc++.h>
using namespace std;
int judge(vector<int> &arr) {
int hash[7] = {0};
for (int &x: arr) hash[x]++;
int cnt3 = 0, cnt2 = 0;
for (int i = 1; i <= 6; i++) {
if (hash[i] == 5) return 8;
if (hash[i] == 4) return 7;
if (hash[i] == 3) cnt3++;
if (hash[i] == 2) cnt2++;
}
if (cnt3 == 1 && cnt2 == 1) return 6;
if (hash[2] == 1 && hash[3] == 1 && hash[4] == 1 && hash[5] == 1 && hash[6] == 1) {
return 5;
}
if (hash[2] == 1 && hash[3] == 1 && hash[4] == 1 && hash[5] == 1 && hash[1] == 1) {
return 4;
}
if (cnt3 == 1) return 3;
if (cnt2 == 2) return 2;
if (cnt2 == 1) return 1;
return 0;
}
int dfs(vector<int> &arr, int s, int id, int mask) {
if (s == 5) {
return judge(arr) > id ? 1 : 0;
}
int acc = 0;
if (((1 << s) & mask) != 0) {
int old = arr[s];
for (int i = 1; i<= 6; i++) {
arr[s] = i;
acc += dfs(arr, s + 1, id, mask);
}
arr[s] = old;
} else {
acc += dfs(arr, s + 1, id, mask);
}
return acc;
}
int bitCount(int v) {
int cnt = 0;
while (v > 0) {
v = v & (v - 1);
cnt++;
}
return cnt;
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
int main() {
int t;
cin >> t;
int times[] = {0, 6, 6 * 6, 6 * 6 * 6,
6 * 6 * 6 * 6,
6 * 6 * 6 * 6 * 6,
6 * 6 * 6 * 6 * 6 * 6};
for (int i = 0; i < t; i++) {
vector<int> arr(5);
for (int &x: arr) cin >> x;
// 判定牌的类型
int id = judge(arr);
// 进行暴力全枚举
int ans = 0;
int fz = 0, mz = 1;
int mask = 1 << 5;
for (int j = 1; j < mask; j++) {
vector<int> brr(arr.begin(), arr.end());
int change = bitCount(j);
int fz2 = dfs(brr, 0, id, j);
int mz2 = times[change];
if (fz * mz2 < fz2 * mz || (fz * mz2 == fz2 * mz && ans > change)) {
ans = change;
fz = fz2;
mz = mz2;
int g = gcd(fz, mz);
fz /= g;
mz /= g;
}
}
cout << ans << " " << fz << " " << mz << '\n';
}
return 0;
}
RC-u4 相对论大师
分值: 25分
考察:图论 + bfs
这题的关键就是图由m个节点n条边构成, n≤1000n \le 1000n≤1000‘
图的节点,由 论点 和 值 这个二元组构成
因为是稀疏图,遍历每个节点,进行bfs即可。
这样总的时间复杂度为 O(n∗m)O(n*m)O(n∗m)
不过写起来确实变扭
#include <bits/stdc++.h>
using namespace std;
struct K {
string k; int v;
K(string k, int v): k(k), v(v) {}
bool operator<(const K& lhs) const {
if (k != lhs.k) return k < lhs.k;
return v < lhs.v;
}
};
int id = 0;
map<K, int> mp;
int ide(K &e) {
if (mp.find(e) != mp.end()) return mp[e];
mp[e] = id++;
return mp[e];
}
int main() {
int n;
cin >> n;
vector<vector<int>> g;
vector<K> vec;
for (int i = 0; i < n; i++) {
string k1; int v1;
string k2; int v2;
cin >> k1 >> v1 >> k2 >> v2;
K e1(k1, v1), e2(k2, v2);
int id1 = ide(e1);
int id2 = ide(e2);
if (id1 >= vec.size()) {
vec.push_back(e1);
g.push_back(vector<int>());
}
if (id2 >= vec.size()) {
vec.push_back(e2);
g.push_back(vector<int>());
}
g[id1].push_back(id2);
}
vector<int> bestAns;
string startK = "";
int startV = -1;
int m = vec.size();
for (int i = 0; i < m; i++) {
int fe = -1;
for (int j = 0; j < m; j++) {
if (vec[i].k == vec[j].k && vec[i].v != vec[j].v) {
fe = j;
break;
}
}
if (fe == -1) continue;
int s = i;
int inf = 0x3f3f3f3f;
vector<int> dp(id, inf);
vector<int> source(id, -1);
deque<int> que;
que.push_back(s);
dp[s] = 0;
while (!que.empty()) {
int u = que.front(); que.pop_front();
for (int v: g[u]) {
if (dp[v] > dp[u] + 1) {
dp[v] = dp[u] + 1;
source[v] = u;
que.push_back(v);
}
}
}
if (source[fe] == -1) continue;
int last = fe;
vector<int> ans;
while (source[last] != -1) {
ans.push_back(last);
last = source[last];
}
ans.push_back(last);
if (startK == "" || bestAns.size() > ans.size()) {
bestAns = ans;
startK = vec[s].k;
startV = vec[s].v;
}
}
reverse(bestAns.begin(), bestAns.end());
for (int i = 0; i < bestAns.size(); i++) {
if (i > 1) {
cout << vec[bestAns[i - 1]].k << " " << vec[bestAns[i - 1]].v << " ";
}
cout << vec[bestAns[i]].k << " " << vec[bestAns[i]].v << " ";
}
cout << "= " << startK << " " << startV << " " << startK << " " << (1 - startV) << "\n"
;
return 0;
}
RC-u5 相对成功与相对失败
分值: 30分
知识点: 思维 + 状态机DP
这题的核心是关系式,即
(1,0)>(1,1)∣(0,0)>(0,1) (1, 0) > (1, 1) | (0, 0) > (0, 1) (1,0)>(1,1)∣(0,0)>(0,1)
(1,1)和(0,0)(1,1) 和 (0,0)(1,1)和(0,0) 没有偏序关系,可以合并为一种状态
那当前的序列中,其实就是划分为这3种状态,使得满足要求元素最多。
这里面有一种朴素的思路
3个状态,2个划分点,枚举2个划分点即可3个状态,2个划分点,枚举2个划分点即可3个状态,2个划分点,枚举2个划分点即可
但这个时间复杂度为O(n2),n≤105O(n^2), n \le 10^5O(n2),n≤105, 显然不行。
那如何求解呢?
可以引入状态机DP
令 s0, s1, s2分别为划分态的最优值
具体状态方程见代码, 整个时间复杂度为O(n)O(n)O(n)
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t-- > 0) {
int n;
cin >> n;
vector<array<int, 2>> arr;
for (int i = 0; i < n; i++) {
int a, b;
cin >> a >> b;
arr.push_back({a, b});
}
vector<int> seq(n);
for (int &x: seq) cin >> x;
// 偏序关系
// (1, 0) > (0, 0) | (1, 1) > (0, 1)
// 可以枚举分界点,但这样是O(n^2)
// 试试 状态机DP
int s0 = 0, s1 = 0, s2 = 0;
for (int x: seq) {
auto e = arr[x - 1];
if (e[0] == 1 && e[1] == 0) {
int t0 = s0 + 1;
int t1 = max(s0, s1);
int t2 = max({s0, s1, s2});
s0 = t0; s1 = t1; s2 = t2;
} else if (e[0] == 0 && e[1] == 1) {
int t0 = s0;
int t1 = max(s0, s1);
int t2 = max({s0, s1, s2}) + 1;
s0 = t0; s1 = t1; s2 = t2;
} else {
int t0 = s0;
int t1 = max(s0, s1) + 1;
int t2 = max({s0, s1, s2});
s0 = t0; s1 = t1; s2 = t2;
}
}
cout << n - max({s0, s1, s2}) << endl;
}
return 0;
}