URL:https://atcoder.jp/contests/abc310
目录
A
Problem/题意
购买一件物品有两种方法:要么用 P 元,要么用 Q 元并且得多买一样菜 Di。
Thought/思路
模拟。
Code/代码
#include "bits/stdc++.h"
signed main() {
int n, p, q; std::cin >> n >> p >> q;
int min = 1e9;
for (int i = 1; i <= n; ++ i) {
int x; std::cin >> x;
min = std::min(x, min);
}
std::cout << std::min(min + q, p);
}
B
Problem/题意
有 N 个产品,每个产品的价格是 Pi,每个产品有 Ci 个功能,用 Fij 来表示。
问是否有一对产品满足下面的条件:
Thought/思路
N 较小,暴力每一种组合。Fij 较小,数组标记每一种产品的功能。最后满足三个条件即可。
Code/代码
#include "bits/stdc++.h"
int n, m, price[107],cnt[107];
std::bitset <107> f[107];
bool check(int i, int j) {
if (price[i] <= price[j]) {
std::bitset<107> t = f[i] & f[j];
if (t == f[j] and (price[i] < price[j] or t != f[i])) {
return true;
}
}
return false;
}
signed main() {
std::cin >> n >> m;
for (int i = 1; i <= n; ++ i) {
std::cin >> price[i] >> cnt[i];
for (int j = 1; j <= cnt[i]; ++ j) {
int x; std::cin >> x;
f[i][x] = 1;
}
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
if (i == j) continue;
if (check(i, j)) {
std::cout << "Yes";
return 0;
}
}
}
std::cout << "No";
return 0;
}
C
Problem/题意
对于一个字符串,它的逆序和正序定义为相同的字符串。
给出 N 个字符串,问有几个字符串不同。
Thought/思路
可以用 set 去重,那么关键就在于如何判断插入条件。
Code/代码
#include "bits/stdc++.h"
signed main() {
int n; std::cin >> n;
std::set <std::string> st;
for (int i = 1; i <= n; ++ i) {
std::string s; std::cin >> s;
if (!st.count(s)) {
std::reverse(s.begin(), s.end());
if (!st.count(s)) {
st.insert(s);
}
}
}
std::cout << st.size();
}
D
Problem/题意
用 N 个运动员组建 T 个队伍,但是队员中有 M 对关系是敌对的,不能放在同一个队伍,问有多少种分配方式。
其中,[1,2]、[3] 和 [3]、[1、2] 是同一种方式。
Thought/思路
N、T 都只有 10,所以可以暴搜。但是直接暴搜就会有 的复杂度,所以要剪枝。
我们发现,[1,2]、[3] 和 [3]、[1、2] 是同一种方式,但 [1,3]、[2] 和 [1,2]、[3] 显然不是同一种。所以,我们可以固定分配的顺序,也就是先分配 1 号运动员,然后分配 2 号运动员。
这样就可以在分配的时候,排除掉存在敌对关系的队伍,将其放入没有敌对关系的队伍,同时也能保证前后队伍之间一定是以最小那号运动员的编号来升序排列。
还有最后一个问题,排列组合问题会有回溯,回溯就会将当前递归的运动员弹出某个队伍,也就有可能导致该队伍没有人,即违反了题目要求,此时要 return。
Code/代码
#include "bits/stdc++.h"
int n, t, m, inc[11][11], ans;
std::vector <int> v[11]; // 每个组包含的人
void dfs(int i) { // 第i个人的分组情况
if (i > n) {
for (int x = 1; x <= t; ++ x) {
if (v[x].size() == 0) return;
}
ans ++;
return;
}
for (int x = 1; x <= t; ++ x) {
bool flag = true;
for (auto &j : v[x]) {
if (inc[i][j] == 1) flag = false;
}
if (flag) {
v[x].push_back(i);
dfs(i + 1);
v[x].pop_back();
}
if (!v[x].size()) return;
}
}
signed main() {
std::cin >> n >> t >> m;
for (int i = 1; i <= m; ++ i) {
int a, b; std::cin >> a >> b;
inc[a][b] = inc[b][a] = 1;
}
dfs(1);
std::cout << ans;
}
E
Problem/题意
有如下公式:
求:
其中, 是一个位运算,意为:
即有 0 则 1,无 0 则 0。
Thought/思路
5
00110
=========
0 1 0 1 1
0 1 0 1
1 0 1
1 1
0
7
0000000
=============
0 1 1 1 1 1 1
0 1 1 1 1 1
0 1 1 1 1
0 1 1 1
0 1 1
0 1
0
观察上面两个例子,就很容易发现递推规律:
a[i] = 0:
dp[i][0] = 1
dp[i][1] = i - 1
a[i] = 1:
dp[i][0] = dp[i - 1][1]
dp[i][1] = dp[i - 1][0] + 1
其中,dp[i][0] 表示当前第 i 个位置有多少个以0结尾的字符串,dp[i][1] 表示当前第 i 个位置有多少个以 1 结尾的字符串。
每次把 dp[i][1] 加起来即可。
Code/代码
#include "bits/stdc++.h"
#define int long long
int a[1000007], n, dp[1000007][2];
char s[1000007];
signed main() {
std::cin >> n >> s + 1;
for (int i = 1; i <= n; ++ i) {
a[i] = s[i] - '0';
}
int ans = 0;
for (int i = 1; i <= n; ++ i) {
if (a[i] == 0) {
dp[i][0] = 1;
dp[i][1] = i - 1;
} else {
dp[i][0] = dp[i - 1][1];
dp[i][1] = dp[i - 1][0] + 1;
}
ans += dp[i][1];
}
std::cout << ans;
}