CSP-J 2024 T1 扑克牌
题目分析
题目给出已有的牌,要求计算最少需要补充多少张牌才能凑齐一副完整的扑克牌(52张)。给出的扑克牌可能包含重复的牌(即花色和点数都相同的牌),因此需要去重后计算缺失的牌数。
代码
#include <iostream>
#include <set>
using namespace std;
int main() {
int n;
cin >> n;
set<string> st;
while (n--) {
string s;
cin >> s;
st.insert(s);
}
cout << 52 - st.size() << endl;
return 0;
}
CSP-J 2024 T2 地图探险
题目大意
有一片丛林,其环境可以用一个二维数组表示:
.代表此地为空地,可走。x代表此地非空地,不可走。
有一个机器人探路,他的状态分为两个值:
- 位置。
- 方向。
机器人探路有这样一些规则:
- 若前方有路,直接往前走(不管是否走过)。
- 遇到障碍物,右转。
现在给出机器人的初始状态(一定在空地),求执行k次操作之后,机器人经过了多少地方(右转算一次操作,前进算一次操作)。
思路
需要的变量 / 数组
描述机器人状态需要的3个变量:
x:位置。y:位置。d:朝向(0~3)。
vis数组:
- 当机器人走过[x, y],
vis[x][y] = true。
dir数组:
dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}。[x + dir[d][0], y + dir[d][1]]表示当机器人朝向为d时往前走一步之后的位置。- d = 0: 右 (0, 1)。
- d = 1: 下 (1, 0)。
- d = 2: 左 (0, -1)。
- d = 3: 上 (-1, 0)。
核心思想
vis[x][y] = true;
while (k--) {
int nx = x + dir[d][0], ny = y + dir[d][1]; // 计算下一步的位置
if (!(nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == '.')) { // 是否能走
d = (d + 1) % 4; // 若不能走 右转
continue;
}
x = nx, y = ny; // 若能走 更新
vis[nx][ny] = true;
}
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3 + 5;
int n, m, k, x, y, d, dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
char mp[N][N];
bool vis[N][N];
int main() {
int t;
cin >> t;
while (t--) {
memset(vis, false, sizeof (vis));
cin >> n >> m >> k >> x >> y >> d;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> mp[i][j];
vis[x][y] = true;
while (k--) {
int nx = x + dir[d][0], ny = y + dir[d][1];
if (!(nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == '.')) {
d = (d + 1) % 4;
continue;
}
x = nx, y = ny;
vis[nx][ny] = true;
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (vis[i][j] == true)
ans++;
}
}
cout << ans << endl;
}
return 0;
}
CSP-J 2024 T2 小木棍
题目大意
小 S 有 n 根小木棍,希望拼出一个正整数,满足如下条件:
- 拼出这个数恰好使用 n 根小木棍。
- 拼出的数没有前导 0。
- 在满足以上两个条件的前提下,这个数尽可能小。
思路
拼出0~9分别需要的木棍数量:
| 数字 | 所需木棍数 |
|---|---|
| 0 | 6 |
| 1 | 2 |
| 2 | 5 |
| 3 | 5 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 3 |
| 8 | 7 |
| 9 | 6 |
求解思想:
- 因为拼出
8所需的木棍最多(7根),所以用8,可以使位数更少 - 在保证位数最少的前提下,还要尽可能把高位的数字变小(必须保证位数最小)
按照思想,可以找到这样的规律:
| 条件 | 构造方案 |
|---|---|
| n == 1 | 无解 |
| n == 2 | 1 |
| n == 3 | 7 |
| n == 4 | 4 |
| n == 5 | 2 |
| n == 6 | 6 |
| n == 7 | 8 |
| n == 10 | 22 |
| (特判除外)n % 7 == 0 | (n / 7)个8 |
| (特判除外)n % 7 == 1 | 10 + ((n - 8) / 7)个8 |
| (特判除外)n % 7 == 2 | 1 + ((n - 2) / 7)个8 |
| (特判除外)n % 7 == 3 | 200 + ((n - 17) / 7)个8 |
| (特判除外)n % 7 == 4 | 20 + ((n - 11) / 7)个8 |
| (特判除外)n % 7 == 5 | 2 + ((n - 5) / 7)个8 |
| (特判除外)n % 7 == 6 | 6 + ((n - 6) / 7)个8 |
代码
#include <iostream>
using namespace std;
string Solve(int n) {
if (n == 1)
return "-1";
if (n % 7 == 0)
return string(n / 7, '8');
if (n % 7 == 1)
return "10" + string((n - 8) / 7, '8');
if (n % 7 == 2)
return "1" + string((n - 2) / 7, '8');
if (n % 7 == 3)
return n == 3 ? "7" : n == 10 ? "22" : "200" + string((n - 17) / 7, '8');
if (n % 7 == 4)
return n == 4 ? "4" : "20" + string((n - 11) / 7, '8');
if (n % 7 == 5)
return "2" + string((n - 5) / 7, '8');
if (n % 7 == 6)
return "6" + string((n - 6) / 7, '8');
}
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
cout << Solve(n) << endl;
}
return 0;
}
CSP-J 2024 T4 接龙
题目大意
小 J 要进行一场 n 个人玩的接龙游戏,游戏的规则和定义如下:
- 每个人都有自己的词库。
- 游戏分为若干轮接龙,一个人连着接龙若干个词语算一轮,相邻两轮接龙不能由同一人完成。
- 第一轮必须从 1 开始接龙,其他情况必须从上一轮的最后一个数开始接龙。
- 每个人接龙都只能从自己的词库选择长度为 1 ~ k 的连续子序列接龙。
- 示例:
- 词库:[1, 2, 3, 5, 4]
- 上一轮结尾:2
- k = 2时可选子序列:
[2] (长度1)
[2,3] (长度2)
现在求能否接龙 r 轮,最后以 c 结尾。
输入样例解释
1 // 数据组数
3 3 7 // 人数 接龙序列长度上限(k) 询问次数
5 1 2 3 4 1 // 第 1 个人的词库
3 1 2 5 // 第 2 个人的词库
3 5 1 6 // 第 3 个人的词库
1 2 // 询问接龙 1 轮、以 2 结尾能否实现
......
解题思路
本文的解题思想为dp。
dp[r][x]:接龙 r 轮、以 x 结尾的状态:
dp[r][x]= -1:不可实现。dp[r][x]= i(一个自然数):可以通过词库 i 实现(最后一次接龙操作使用的是词库 i 中的某个连续子序列)。dp[r][x]= -2:可以通过多个词库实现。
代码解析
完整的求解部分:
vector<vector<int> > Solve(int mx) {
vector<vector<int> > dp(R, vector<int>(mx + 5, -1));
dp[0][1] = -2;
for (int r = 1; r <= 100; r++) {
for (int i = 1; i <= n; i++) {
int lst = -1;
for (int j = 0; j < s[i].size(); j++) {
int tmp = s[i][j];
if (lst != -1 && j - lst + 1 <= k) {
if (dp[r][tmp] == -1)
dp[r][tmp] = i;
else if (dp[r][tmp] != i)
dp[r][tmp] = -2;
}
if (dp[r - 1][tmp] != -1 && dp[r - 1][tmp] != i)
lst = j;
}
}
}
return dp;
}
逐行分析
- mx:所有词库中的最大值,用于构建dp的第二个维度。
- dp的大小为
R(最大轮数) * mx(所有词库中的最大值)。
vector<vector<int> > Solve(int mx) {
vector<vector<int> > dp(R, vector<int>(mx + 5, -1));
dp[0][1] = -2:作为虚拟起点,使得第一次接龙可以从数字 1 开始。
dp[0][1] = -2;
- r:轮数。
- i:尝试让第 i 个人接龙。
- j:第 i 个人词库中第 j 个词语。
- lst:当前序列 i 中最近一个满足接龙条件的起始位置(可以以lst为起点开始接龙)。
for (int r = 1; r <= 100; r++) {
for (int i = 1; i <= n; i++) {
int lst = -1;
for (int j = 0; j < s[i].size(); j++) {
}
}
}
- 获取当前人员 i 的词库中第 j 个词语。
- 如果当前词库中有一个合适的起点可以开始接龙,并且长度合适:
- 那么选择子序列[lst, j]开始接龙。
- 更新当前状态。
- 如果上一轮接龙不是 i 在接龙,并且可以以tmp结束,那么这一轮也可以从tmp开始接龙。
int tmp = s[i][j];
if (lst != -1 && j - lst + 1 <= k) {
if (dp[r][tmp] == -1)
dp[r][tmp] = i; // 首次发现路径则记录序列i
else if (dp[r][tmp] != i)
dp[r][tmp] = -2; // 若已有其他序列可达则标记为多路径(-2)
}
if (dp[r - 1][tmp] != -1 && dp[r - 1][tmp] != i)
lst = j;
完整代码
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 5, R = 105;
vector<int> s[N];
int n, k, q;
vector<vector<int> > Solve(int mx) {
vector<vector<int> > dp(R, vector<int>(mx + 5, -1));
dp[0][1] = -2;
for (int r = 1; r <= 100; r++) {
for (int i = 1; i <= n; i++) {
int lst = -1;
for (int j = 0; j < s[i].size(); j++) {
int tmp = s[i][j];
if (lst != -1 && j - lst + 1 <= k) {
if (dp[r][tmp] == -1)
dp[r][tmp] = i;
else if (dp[r][tmp] != i)
dp[r][tmp] = -2;
}
if (dp[r - 1][tmp] != -1 && dp[r - 1][tmp] != i)
lst = j;
}
}
}
return dp;
}
int main() {
int t;
cin >> t;
while (t--) {
int mx = -1;
cin >> n >> k >> q;
for (int i = 1; i <= n; i++) {
int l;
cin >> l;
s[i].clear();
s[i].resize(l);
for (int j = 0; j < l; j++) {
cin >> s[i][j];
mx = max(mx, s[i][j]);
}
}
vector<vector<int> > dp = Solve(mx);
while (q--) {
int r, c;
cin >> r >> c;
cout << (dp[r][c] != -1 ? 1 : 0) << endl;
}
}
return 0;
}



5287

被折叠的 条评论
为什么被折叠?



