AtCoder Beginner Contest 328(A~G)

文章介绍了五道编程题目,涉及字符串比较、时间复杂度分析、并查集操作、状态压缩的动态规划方法和最小生成树问题的修改。展示了在AtCoderBeginnerContest中的不同挑战和解决方案策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AtCoder Beginner Contest 328

A - Not Too Hard

就是比较大小然后把满足条件的值加进来

时间复杂度 O ( N ) O(N) O(N)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N, X;
	std::cin >> N >> X;

	int ans = 0;
	for (int i = 0; i < N; i++) {
		int x;
		std::cin >> x;
		if (x <= X) {
			ans += x;
		}
	}

	std::cout << ans << "\n";

	return 0;
}
B - 11/11

这道题我做的时候很麻烦,甚至还把每一位都拆出来了,实际上只要把月份和当前的是哪一天转成字符串加起来比较一下整个串的每个字符是否都等于这个这个串的第一个字符就好了。

时间复杂度 O ( N D ) O(ND) O(ND)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N;
	std::cin >> N;

	int ans = 0;
	for (int i = 1; i <= N; i++) {
		int D;
		std::cin >> D;
		for (int j = 1; j <= D; j++) {
			auto s = std::to_string(i) + std::to_string(j);
			if (s == std::string(int(s.size()), s[0])) {
				ans += 1;
			}
		}
	}

	std::cout << ans << "\n";
	return 0;
}
C - Consecutive

看一个区间内满足 s i = s i + 1 s_i = s_{i + 1} si=si+1条件的字符有多少个,显然就是要做一个前缀和,这里注意不能把这个区间内最后一个字符放进去。

时间复杂度 O ( N + Q ) O(N + Q) O(N+Q)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N, Q;
	std::cin >> N >> Q;

	std::string S;
	std::cin >> S;
	S.push_back(' ');

	std::vector<int> sum(N + 1);
	for (int i = 0; i < N; i++) {
		sum[i + 1] = sum[i] + (S[i] == S[i + 1]);
	}
	while (Q--) {
		int l, r;
		std::cin >> l >> r;
		l--, r--;

		std::cout << sum[r] - sum[l] << "\n";
	}

	return 0;
}
D - Take ABC

从左边开始每次都删除ABC这个字符串,每次都从左边删,其实已经暗示的非常明显了,就是要用栈。每次字符串后端为ABC时就删掉。

时间复杂度 O ( 3 N ) O(3N) O(3N)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	std::string S;
	std::cin >> S;

	std::string T;

	for (auto c : S) {
		T += c;

		while (T.size() >= 3 && T.substr(int(T.size()) - 3) == "ABC") {
			T.resize(int(T.size()) - 3);
		}
	}

	std::cout << T << "\n";
	return 0;
}
E - Modulo MST

题目是要求一个最小生成树,但是是在模数意义下的,因此就不能靠平时用的最小生成树算法来解决了。

数据范围 1 ≤ M ≤ 28 1 \le M \le 28 1M28,看起来就是可以爆搜的,但是要考虑剪枝,由于生成树最多有 n − 1 n-1 n1条边,所以其实最多就会枚举7条边,时间复杂度上限为KaTeX parse error: Undefined control sequence: \C at position 3: O(\̲C̲_{28}^{7})

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N, M;
	i64 K;
	std::cin >> N >> M >> K;

	std::vector<std::tuple<int, int, i64>> e(M);
	for (int i = 0; i < M; i++) {
		int u, v;
		i64 w;
		std::cin >> u >> v >> w;

		u--, v--;
		e[i] = {u, v, w};
	}

	i64 ans = i64(1E15);
	int u, v;
	i64 w;
	for (int s = 0; s < (1 << M); s++) {
		i64 res = 0;
		DSU dsu(N);
		for (int j = 0; j < M; j++) {
			std::tie(u, v, w) = e[j];
			if (s >> j & 1) {
				if (!dsu.same(u, v)) {
					dsu.merge(u, v);
					res = (res + w) % K;
				} else {
					break;
				}
			}
		}
		if (dsu.size(0) == N) {
			ans = std::min(ans, res);
		}
	}
	std::cout << ans << "\n";
	return 0;
}
F - Good Set Query

带权并查集非常经典的板子题,就是给两条边连线的时候比较关键。

可以参考带权并查集进行学习,文章写的非常棒。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N, Q;
	std::cin >> N >> Q;

	std::vector<int> f(N);
	std::iota(f.begin(), f.end(), 0);
	std::vector<i64> dis(N);

	auto find = [&](auto self, int x) -> int {
		if (x == f[x]) {
			return x;
		}

		int y = self(self, f[x]);
		dis[x] += dis[f[x]];
		f[x] = y;

		return y;
	};

	for (int i = 1; i <= Q; i++) {
		int a, b, d;
		std::cin >> a >> b >> d;
		a--, b--;

		int x = find(find, a);
		int y = find(find, b);

		if (x != y) {
			dis[x] = dis[b] + d - dis[a];
			f[x] = y;
			std::cout << i << " ";
		} else if (dis[a] - d == dis[b]) {
			std::cout << i << " ";
		}
	}

	return 0;
}
G - Cut and Reorder

1 ≤ N ≤ 22 1 \le N \le 22 1N22的数据范围容易想到可能会是个状态压缩的DP。

题目中给出的两种操作实际上是相互独立的,于是可以考虑DP。

普通地,我们考虑设计DP状态,记 d p [ i ] [ s ] dp[i][s] dp[i][s]为前 i i i个数,匹配状态为 s s s的最小代价( s s s在二进制下1代表匹配,0代表未匹配)

我们既要考虑单个去匹配又要考虑成块去匹配,这一步操作的复杂度是 O ( N 2 ) O(N^{2}) O(N2),所以总的时间复杂度为 O ( 2 N N 3 ) O(2^{N}N^{3}) O(2NN3),这样显然是会超时的。

但是注意到我们枚举状态 s s s是从 0 → 2 n 0 \to 2^{n} 02n,其中二进制中1的个数是非严格递增的,因此可以借此去优化掉一维枚举。

于是记 d p [ s ] dp[s] dp[s]为当前 a a a数组中前 p o p c o u n t ( s ) popcount(s) popcount(s)个数已经与以 s s s的二进制中1的位置为下标的 b b b数组中元素相匹配。

因此每匹配一部分就要加上这一部分数字移动的代价以及达到匹配效果所需要的差值。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N;
    i64 C;
    std::cin >> N >> C;

    std::vector<i64> A(N);
    for (int i = 0; i < N; i++) {
        std::cin >> A[i];
    }
    std::vector<i64> B(N);
    for (int i = 0; i < N; i++) {
        std::cin >> B[i];
    }

    std::vector<i64> dp(1 << N, 1E18);
    dp[0] = -C;

    for (int s = 0; s < (1 << N); s++) {
        int c = __builtin_popcount(s);

        for (int i = 0; i < N; i++) {
            i64 val = 0;
            int t = s;
            if (~s >> i & 1) {
                for (int j = i; j < N; j++) {
                    if (s >> j & 1) {
                        break;
                    }

                    t |= 1 << j;
                    val += std::abs(A[c + j - i] - B[j]);
                    dp[t] = std::min(dp[t], dp[s] + val + C);
                }
            }
        }
    }

    std::cout << dp.back() << "\n";

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值