A. 输出字符
给定一个长度为 lenlenlen 的数字串,补全前缀零,使得输出串长度为 444
#include<bits/stdc++.h>
using namespace std;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
string s;
cin >> s;
for (int i = 0; i < 4 - (int) s.size(); i++) {
cout << '0';
}
cout << s << endl;
return 0;
}
B. 简单统计
给定数组 aaa 和数 ppp,统计 aaa 中有多少个数 <p<p<p
循环一边即可
复杂度:O(N)O(N)O(N)
#include<bits/stdc++.h>
using namespace std;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, p, x, cnt = 0;
cin >> n >> p;
for (int i = 1; i <= n; i++) {
cin >> x;
cnt += x < p;
}
cout << cnt << endl;
return 0;
}
C. 模拟
需要记录当前所有人的状态,即 (x,p)(x, p)(x,p),xxx 表示标号为 ppp 的人当前的分数
每次需要进行排序,以 xxx 降序,如果 xxx 相等,按 ppp 增序,模拟一遍过程即可
#include<bits/stdc++.h>
using namespace std;
string a[110];
int ans[110];
bool cmp(pair<int, int> a, pair<int, int> b) {
if (a.first == b.first) return a.second < b.second;
else return a.first > b.first;
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m;
vector<pair<int, int>> vt;
cin >> n >> m;
for (int i = 0; i < n * 2; i++) {
cin >> a[i];
vt.push_back({0, i});
}
for (int i = 0; i < m; i++) {
sort(vt.begin(), vt.end(), cmp);
for (int j = 0; j < n; j++) {
int p1 = vt[2 * j].second, p2 = vt[2 * j + 1].second;
char cha = a[p1][i], chb = a[p2][i];
if (cha == chb) {
continue;
}
else {
string v;
v += cha; v += chb;
if (v == "GC" || v == "CP" || v == "PG"){
vt[2 * j].first++;
}
else {
vt[2 * j + 1].first++;
}
}
}
}
sort(vt.begin(), vt.end(), cmp);
for (auto it: vt) {
cout << it.second + 1 << endl;
}
return 0;
}
D. 动态规划
很容易容易想到一个 dpdpdp
dp[i][j]dp[i][j]dp[i][j] 表示前 iii 个数,最后一个数取到的值为 jjj,所有满足条件的总情况数
那么有以下转移:
- dp[i][j]←∑k=1jdp[i−1][k]dp[i][j]\leftarrow \sum\limits_{k = 1}^{j}dp[i - 1][k]dp[i][j]←k=1∑jdp[i−1][k]
那么显然总复杂度是:O(NM2)O(NM^2)O(NM2),其中 M=maxA,BM = \max {A, B}M=maxA,B
这里 ∑k=1jdp[i−1][k]\sum\limits_{k = 1}^{j}dp[i - 1][k]k=1∑jdp[i−1][k] 是前缀和的形式,我们把 dp[i][j]dp[i][j]dp[i][j] 重新定义为前缀和的形式就可以 O(1)O(1)O(1) 转移了
那么转移变为 dp[i][j]←dp[i−1][j]+dp[i][j−1]dp[i][j] \leftarrow dp[i - 1][j] + dp[i][j - 1]dp[i][j]←dp[i−1][j]+dp[i][j−1]
#include<bits/stdc++.h>
using namespace std;
const int N = 3e3 + 5, mod = 998244353;
int dp[N][N], a[N], b[N];
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
fill(dp[0], dp[0] + N, 1);
for (int i = 1; i <= n; i++) {
for (int j = a[i]; j <= b[i]; j++) {
dp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % mod;
}
for (int j = b[i] + 1; j < N; j++) {
dp[i][j] = dp[i][j - 1];
}
}
cout << dp[n][N - 1] << endl;
return 0;
}
E. dfs 求路径 + dp
首先我们需要对于每条边,这条边被走过了多少次
我们可以 MMM 次 dfs, O(NM)O(NM)O(NM) 次的复杂度来求得
比如现在我们知道了每条边经过了多少次,计入 vvv 数组中,∣v∣=N−1|v| = N - 1∣v∣=N−1
那么每条边图成蓝色意味着权值为 +1+1+1,否则权值为 −1-1−1
现在需要给这 N−1N - 1N−1 个数分配 +1+1+1 或 −1-1−1 的权值,来使得和为 KKK,我们需要知道有多少种分配方式
每条路径的贡献是最多是 N−1N - 1N−1 那么 MMM 次贡献最多是 (N−1)M(N - 1)M(N−1)M,那么有 ∑v≤(N−1)M\sum\limits v \leq (N - 1)M∑v≤(N−1)M
我们可以知道这 N−1N - 1N−1 个数通过加上 +1+1+1 或 −1-1−1 的权值之后最大表示的范围是 [−(N−1)M,(N−1)M][-(N - 1)M, (N - 1)M][−(N−1)M,(N−1)M]
那么我们可以 dpdpdp 来求所有可能的值以及情况数了
这个过程和 DDD 题很像
考虑到真实情况是非常稀疏的,所以可以考虑用 mapmapmap 来转移(事实上确实跑得比较快)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5, mod = 998244353;
int n, m, k, fg;
vector<int> nxt[N];
int a[N];
map<pair<int, int>, int> cnt;
vector<int> path, ans;
map<int, int> pre, now;
void dfs(int now, int fa, int to) {
path.push_back(now);
if (now == to) {
ans = path;
return;
}
for (auto it: nxt[now]) {
if (it == fa) continue;
dfs(it, now, to);
}
path.pop_back();
}
void solve(int x, int y) {
fg = 0;
path.clear();
ans.clear();
dfs(x, -1, y);
for (int i = 1; i < (int) ans.size(); i++) {
int u = ans[i - 1], v = ans[i];
cnt[{u, v}]++;
cnt[{v, u}]++;
}
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
cin >> a[i];
}
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
nxt[u].push_back(v);
nxt[v].push_back(u);
}
for (int i = 1; i < m; i++) {
solve(a[i], a[i + 1]);
}
vector<int> vv;
for (int i = 1; i <= n; i++) {
for (auto it: nxt[i]) {
if (it > i) vv.push_back(cnt[{i, it}]);
}
}
// for (auto it: vv) {
// cout << it << " ";
// }
// cout << endl;
now[0] = 1;
for (auto val: vv) {
pre = now;
now.clear();
for (auto it: pre) {
now[it.first - val] += pre[it.first];
now[it.first - val] %= mod;
now[it.first + val] += pre[it.first];
now[it.first + val] %= mod;
}
}
cout << (now[k] + mod) % mod << endl;
return 0;
}
本文涵盖了编程中的几个核心问题,包括字符串前缀补零、简单统计、模拟比赛过程、动态规划求解以及DFS路径计数。通过C++代码展示了如何高效地解决这些问题,涉及动态规划、排序、遍历等技术。

267

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



