AtCoder Beginner Contest 222

本文涵盖了编程中的几个核心问题,包括字符串前缀补零、简单统计、模拟比赛过程、动态规划求解以及DFS路径计数。通过C++代码展示了如何高效地解决这些问题,涉及动态规划、排序、遍历等技术。

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=1jdp[i1][k]

那么显然总复杂度是:O(NM2)O(NM^2)O(NM2),其中 M=max⁡A,BM = \max {A, B}M=maxA,B

这里 ∑k=1jdp[i−1][k]\sum\limits_{k = 1}^{j}dp[i - 1][k]k=1jdp[i1][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[i1][j]+dp[i][j1]

#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 - 1v=N1

那么每条边图成蓝色意味着权值为 +1+1+1,否则权值为 −1-11

现在需要给这 N−1N - 1N1 个数分配 +1+1+1−1-11 的权值,来使得和为 KKK,我们需要知道有多少种分配方式

每条路径的贡献是最多是 N−1N - 1N1 那么 MMM 次贡献最多是 (N−1)M(N - 1)M(N1)M,那么有 ∑v≤(N−1)M\sum\limits v \leq (N - 1)Mv(N1)M

我们可以知道这 N−1N - 1N1 个数通过加上 +1+1+1−1-11 的权值之后最大表示的范围是 [−(N−1)M,(N−1)M][-(N - 1)M, (N - 1)M][(N1)M,(N1)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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值