CSP-J 2024 题解
CSP-J 2024 扑克牌
用 set 可以轻松判断扑克牌的种类数,然后用 52 减去它
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
set<string> s;
while(n--) {
string c;
cin >> c;
s.insert(c);
}
cout << 52 - s.size() << '\n';
}
CSP-J 2024 地图探险
比较常规的模拟题,唯一需要注意的是结束/开始时的位置要加入统计
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
auto solve = []() {
int n, m, k;
cin >> n >> m >> k;
int x, y, d;
cin >> x >> y >> d;
x--, y--;
vector<string> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
vector vis(n, vector<int>(m));
int ans = 0;
while (k--) {
if (!vis[x][y]) {
ans++;
vis[x][y] = 1;
}
int nx = x, ny = y;
if (d == 0) ny++;
if (d == 1) nx++;
if (d == 2) ny--;
if (d == 3) nx--;
if (nx >= 0 && nx < n && ny >= 0 && ny < m && a[nx][ny] == '.') {
x = nx, y = ny;
} else {
d = (d + 1) % 4;
}
}
if (!vis[x][y]) ans++;
cout << ans << '\n';
};
while (t--) solve();
}
CSP-J 2024 小木棍
很有意思的贪心和模拟题。首先要自己列出0-9需要的木棍数,发现8是最占用木棍的,假如木棍很多那肯定大部分数是8,这样拼出来的数数位最少。顺着这个思路,我们可以先确定拼出来的数的数位是 \(\lceil \frac{k}{7} \rceil\)。确定好数位了,我们就从头到尾贪心地取“能取的最小值”,所谓“能取的最小值”其实就是确保后面的数也可以组出一个合法的数字。具体的条件见代码。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
const vector<int> cost = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
int t;
cin >> t;
auto solve = [&]() {
int n;
cin >> n;
if (n == 1) {
cout << "-1\n";
return;
}
int k = (n + 6) / 7;
string ans;
for (int i = 0; i < k; i++) {
int left = k - i - 1;
int start = int(i == 0);
for (int j = start; j <= 9; j++) {
int after = n - cost[j];
if ((!left && !after) ||
(left && after >= 2 * left && after <= 7 * left)) {
ans += char('0' + j);
n = after;
break;
}
}
}
cout << ans << '\n';
};
while (t--) solve();
}
CSP-J 2024 接龙
老实讲这题真是难到我了。第一眼看过去很容易被唬住。其实本质是个按轮数递推的 DP,但状态设计非常考验对冗余信息的剥离。
题目最烦的限制是“相邻两轮不能是同一个人”。如果按部就班地用 set 记录“到底有哪几个人能凑出数字 x”,状态和转移都会爆炸。
但仔细一想有个核心性质:如果上一轮凑出数字 x 的人 \(\ge 2\),那下一轮任何人都可以接(因为总能挑到一个不是自己的前驱)。
所以状态直接降维,dp[i][x] 只需要存三种值:
-1:没人能凑出。p:只有玩家p一个人能凑出(下一轮p不能接)。-2:有两人及以上能凑出(下一轮谁都能接,百搭状态)。
另外,转移时如果每次回头找 \(k-1\) 个数肯定会 TLE。其实只要在遍历序列时,顺手维护一个 last_valid_pos(最近一次出现的合法开头位置)就行了。只要当前位置和它的距离 \(\le k\),就能作为这一轮的结尾。
时间复杂度 \(O(R \times \sum L)\)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
auto solve = []() {
int n, k, q;
cin >> n >> k >> q;
vector<vector<int>> s(n);
int max_val = 0;
for (int i = 0; i < n; i++) {
int len;
cin >> len;
while (len--) {
int x;
cin >> x;
x--;
s[i].push_back(x);
max_val = max(max_val, x);
}
}
auto upd = [](int& x, int y) {
if (x == -1)
x = y;
else if (x >= 0 && x != y)
x = -2;
};
vector dp(101, vector<int>(max_val + 1, -1));
// dp[i][j]: 第 i 轮转移是以数字 j 结尾的状态
// p: 第 p 个人可以提供, -1: 无人提供, -2: 超过两个人可以提供
dp[0][0] = -2;
for (int i = 1; i <= 100; i++) {
for (int j = 0; j < n; j++) {
int last_valid_pos = -1;
for (int x = 0; x < s[j].size(); x++) {
if (last_valid_pos != -1 && x - last_valid_pos + 1 <= k) {
upd(dp[i][s[j][x]], j);
}
if (dp[i - 1][s[j][x]] == -2 ||
(dp[i - 1][s[j][x]] >= 0 && dp[i - 1][s[j][x]] != j))
last_valid_pos = x;
}
}
}
while (q--) {
int r, c;
cin >> r >> c;
c--;
if (c > max_val) {
cout << "0\n";
} else {
cout << int(dp[r][c] != -1) << '\n';
}
}
};
while (t--) solve();
}

浙公网安备 33010602011771号