CSP2023-J2参考解析
[CSP-J 2023] 小苹果
小 Y 的桌子上放着 \(n\) 个苹果从左到右排成一列,编号为从 \(1\) 到 \(n\)。
小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 \(1\) 个苹果开始、每隔 \(2\) 个苹果拿走 \(1\) 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 \(n\) 的苹果是在第几天被拿走的?
分析
解法1:模拟过程,大概有50分,加上一些乱搞做法可以优化到80+分
解法2:正解,模拟一下样例,找找数学规律
问题1:所求的是天数,每3个看作一个周期,每次拿走的是 \(\lceil n/3 \rceil\);
问题2,找规律发现每次一定是当元素位序 x%3==1是才被拿走,而最开始的最后一个元素在每次拿走元素后要么被拿走,要么是新序列的最后一个,所以其位序不会对结构产生影响,只需要记录元素个数即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
void brute_force() {
int n; cin >> n;
int a = 0, b = 0, t = n;
vector<int> st(n + 1);
for (int i = 1; i <= n; i++) st[i] = i;
int cnt = 0;
while (cnt < n) {
int i = 1, p = 1;
while (i <= n) {
if (st[i] == 0) i++;
else if (st[i] && p % 3 != 1) i++, p++;
else if (st[i] && p % 3 == 1) {
if (i == n) b = a + 1;
st[i++] = 0, p++, cnt++;
}
}
a++;
}
cout << a << " " << b << endl;
}
void solve() {
int n; cin >> n;
int a = 0, b = 0;
while (n > 2) {
a++;
if (!b && n % 3 == 1) b = a;
n -= ceil(n / 3.0);
}
a += n;
if (b == 0) b = a;
cout << a << " " << b;
}
int main() {
freopen("apple.in", "r", stdin);
freopen("apple.out", "w", stdout);
solve();
fclose(stdin), fclose(stdout);
return 0;
}
[CSP-J 2023] 公路
小苞准备开着车沿着公路自驾。
公路上一共有 \(n\) 个站点,编号为从 \(1\) 到 \(n\)。其中站点 \(i\) 与站点 \(i + 1\) 的距离为 \(v_i\) 公里。
公路上每个站点都可以加油,编号为 \(i\) 的站点一升油的价格为 \(a_i\) 元,且每个站点只出售整数升的油。
小苞想从站点 \(1\) 开车到站点 \(n\),一开始小苞在站点 \(1\) 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 \(d\) 公里。问小苞从站点 \(1\) 开到站点 \(n\),至少要花多少钱加油?
分析
贪心:每次购买当前价格最低的油。
具体步骤:
- 对于每个站点,维护一个 \([1,i]\) 的最小油价站点 \(id\),res[i] 记录第 i 个站点的购买量.
- 对于当前位置 i 到达下一个站点的距离 \(v[i]\),需要的油量 \(t=\lceil v[i]/id \rceil\).
- 可以跑的距离 为 \(t*d\),可能会跑到 \(i+1\) 站点后面,所以对 \(v[i+1] -= d*t - v[i]\),也可能跑到 \(i+2\) 站点后面,所以同时对 \(i+1, i+2\) 维护一个合理的大小(≥0)。
- 答案汇总, \(ans=\sum_{i=1}^{n-1} res[i]*a[i]\).
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
int n, d;
int v[N], a[N], res[N];
void solve() {
scanf("%d%d", &n, &d);
for (int i = 1; i < n; i++) scanf("%d", &v[i]);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int id = 0; // low_price_id
for (int i = 1; i < n; i++) {
if (i == 1 || a[id] > a[i]) id = i;
int t = ceil(1.0 * v[i] / d); // 需要油量
res[id] += t;
v[i + 1] -= d * t - v[i];
if (v[i + 1] < 0) {
int c = v[i + 1];
v[i + 1] = 0;
v[i + 2] += c;
}
}
LL ans = 0;
for (int i = 1; i < n; i++) ans += (LL)res[i] * a[i];
cout << ans;
}
int main() {
freopen("road.in", "r", stdin);
freopen("road.out", "w", stdout);
solve();
fclose(stdin), fclose(stdout);
return 0;
}
[CSP-J 2023] 一元二次方程
分析 一道不简单的模拟题目,就是注意细节,由于是多组数据,不好骗分。
其实这个题目的描述仅仅是对问题进行了细化,主要注意一句话:分母不为负数。
对于后续的操作,我们发现输入的 a出现的位置多为分母,所以进行转换,如果方程系数中 \(a\) 为负数,则 \(a,b,c\) 同时乘上 \(-1\).
其余没有什么坑点,就是多种情况的罗列了,具体看代码。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
void solve() {
int t, m, a, b, c;
cin >> t >> m;
while (t--) {
cin >> a >> b >> c;
if(a < 0) a*=-1, b*=-1, c*=-1;
int delta = b * b - 4 * a * c;
if (delta < 0) cout << "NO" << endl;
else {
int n = delta;// 对 delta 质因数分解
map<int, int> mp;
for (int i = 2; i <= n; i++) {
int p = 0;
while (n && n % i == 0) n /= i, p++;
if (p) mp[i] = p;
}
if (n) mp[n] = 1;
int q1 = -b, q2 = delta > 0, r = delta > 0;
for (auto u : mp) {
int x = u.first, y = u.second;
if (y & 1) r *= x;
q2 *= pow(x, y / 2);
}
// cout<<"test: "<<q1<<" "<<q2<<" "<<r<<" "<<delta<<endl;
int d1 = gcd(q1, 2 * a), d2 = gcd(q2, 2 * a);
if (r <= 1) {
int p = q1 + q2, q = 2 * a, d = gcd(p, q);
p /= d, q /= d;
if (q < 0) p *= -1, q *= -1;
if (p == 0) { cout<<0<<endl; continue; }
cout << p;
if(q!=1) cout<<"/" << q;
cout<< endl;
} else {
int p = q1, q = 2 * a;
p /= d1, q /= d1;
if (q < 0) p *= -1, q *= -1;
if (p != 0) {
cout << p;
if (q != 1) cout << "/" << q;
}
int f = p;
p = q2, q = 2 * a, p /= d2, q /= d2;
if (q < 0) p *= -1, q *= -1;
if (f != 0 && p > 0) cout << "+";
else if (p == -1) cout << "-";
if(abs(p) != 1) cout << p;
if (p > 1) cout << "*";
cout << "sqrt(" << r << ")";
if (q != 1) cout << "/" << q;
cout << endl;
}
}
}
}
int main() {
freopen("uqe.in", "r", stdin);
freopen("uqe.out", "w", stdout);
solve();
fclose(stdin), fclose(stdout);
return 0;
}
[CSP-J 2023] 旅游巴士
最短路的基础上多了条件
- 起点和终点时间为 k 的倍数.
- 通过i点的时间不能 < a[i].
分析
部分分分析
如果无解输出 -1,一般会出现一个点,不然这句话就白说了,直接输出 -1 就有 5分。
\(k=1\) 也就是任意时刻都可以到起点和终点。
\(a[i]=0\) 也就是任意时刻都可以通过i点。
满分解法
最短路+分层图
bfs,spfa,dijkstra
在实际测试中,spfa的效率通常会比dijkstra要高,当然它可能死 O(n)~O(nm).
点击查看代码
#include <bits/stdc++.h>
#include <type_traits>
#include <variant>
using namespace std;
#define PII pair<int, int>
typedef long long LL;
const int N = 1e4 + 10, K = 110, INF = 0x3f3f3f3f;
int n, m, k, h[N], idx, dis[N][K];
bool st[N][K];
struct T {
int v, w, nx;
} g[N << 1];
void add(int u, int v, int w) {
g[++idx] = {v, w, h[u]}, h[u] = idx;
}
int dijkstra() {
memset(dis, 0x3f, sizeof dis);
priority_queue<PII, vector<PII>, greater<PII>> q;
dis[1][0] = 0, q.push({0, 1});
while (q.size()) {
auto it = q.top(); q.pop();
int x = it.first % k, u = it.second;
if (st[u][x]) continue;
st[u][x] = 1;
for (int i = h[u]; i; i = g[i].nx) {
int v = g[i].v, w = g[i].w;
int tv = max((w - x - 1 + k) / k * k + x, dis[u][x]);
int& tt = dis[v][(x + 1) % k];
if (tt > tv + 1) q.push({tt = tv + 1, v});
}
}
return dis[n][0] == INF ? -1 : dis[n][0];
}
int spfa() {
memset(dis, 0x3f, sizeof dis);
queue<PII> q;
dis[1][0] = 0, st[1][0] = 1, q.push({1, 0});
while (q.size()) {
auto it = q.front(); q.pop();
int u = it.first, x = it.second;
st[u][x] = 0;
for (int i = h[u]; i; i = g[i].nx) {
int v = g[i].v, w = g[i].w, y = (x + 1) % k;
int tv = max((w - x - 1 + k) / k * k + x, dis[u][x]);
int& tt = dis[v][y];
if (tt > tv + 1) {
tt = tv + 1;
if (!st[v][y]) st[v][y] = 1, q.push({v, y});
}
}
}
return dis[n][0] == INF ? -1 : dis[n][0];
}
void solve() {
cin >> n >> m >> k;
int u, v, w;
while (m--) cin >> u >> v >> w, add(u, v, w);
cout << spfa() << endl;
// cout << dijkstra()<<endl;
}
int main() {
freopen("bus.in", "r", stdin);
freopen("bus.out", "w", stdout);
solve();
fclose(stdin), fclose(stdout);
return 0;
}
本套测试题暴露的几个值得分析的问题
- T1,T4 很多 MLE,数组开大了,同学们要学习计算空间大小,不要盲开.
- T1 有很多递归实现的,int无返回值.
- T2 局部变量未初始化,O2优化后会出现问题.
- T3 sqrt写成sprt, gcd写的暴力.
- 文件保存位置不对,导致没有收取到选手文件
- 文件输入输出写错