AtCoder Beginner Contest 338
B - Frequency
难度: ⭐
题目大意
给定一个字符串, 输出出现次数最多的字母; 相同就输出字典序更小的;
解题思路
打暴力就行, 没啥好说的;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m;
int p[N];
signed main() {
IOS;
string s;
cin >> s;
char res;
int maxn = 0;
for(int i = 0; i < s.size(); i++){
p[s[i]]++;
if(p[s[i]] > maxn){
maxn = p[s[i]];
res = s[i];
}
else if(p[s[i]] == maxn && s[i] < res){
res = s[i];
}
}
cout << res;
return 0;
}
C - Leftover Recipes
难度: ⭐⭐
题目大意
现在有n种食材, 每种食材各有qi个; 现在有两种菜品A, B; 制作A需要每种食材各ai个, 制作B需要每种食材各ai个; 请问利用现有的食材数量最多可以制作多少个菜品;
解题思路
因为n的范围只有10, 而且qi的范围是1e6, 也就是说最多制作1e6个菜品; 那直接打暴力就好了; 枚举菜品A的数量, 然后遍历n种食材, 计算出剩下的食材最多可以制作多少个B食材; 最后加起来取最大值即可;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m;
int q[N], a[N], b[N];
signed main() {
IOS;
cin >> n;
for(int i = 1; i <= n; i++) cin >> q[i];
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
int res = 0;
for(int i = 0; i <= 1e6; i++){
int num = 1e9;
bool f = true;
for(int j = 1; j <= n; j++){
int x = q[j] - i * a[j];
if(x < 0){
f = false;
break;
}
if(b[j]){
x /= b[j];
num = min(num, x);
}
}
if(f) res = max(res, i + num);
else break;
}
cout << res;
return 0;
}
D - Island Tour
难度: ⭐⭐⭐⭐
题目大意
Atcoder国里有n个小岛, 第i个岛和第i + 1个岛有一座双向桥连接, 第1个岛和第n个岛有桥连接; 小莫想按照x1, x2, x3 .. xm的顺序依次游览m座岛; 在游览过程种运行经过未在计划中的岛屿;
但现需要断开一座桥; 问在任意选择一座桥断开的情况下, 完成游览最少要通过多少次桥; 我们就把经过次数称为路径长度;
解题思路
既然要断掉一座桥, 那么我们可以计算出断掉某座桥的代价, 断掉代价最小的桥即可;
先考虑两点之间的路径, 从xi到xj有两种方式, 一是直接从min岛到max岛; 而是通过1-n桥反方向过去, 两种路径恰好互补; 肯定优先走其中较短的那个, 不妨设较短的路径长度为len; 如果该路径有桥断了, 那么就会被迫去走另一条路, 路径长度为n - len; 那么断掉那个桥的代价就是多走的路径: (n - len) - len, 也就是n - 2 * len; 较短路径中每座桥都会有这样的一个代价;
而题目有m个岛屿, 也就是会重复m - 1次上述情况, 每座桥的代价是累积的, 最后找出总代价最小的桥即可; 每次给一个区间加一个数, 很明显的差分操作; 最后是要求前缀和, 再加上前面的单点修改, 所以可以想到用树状数组;
因为上述操作都是对桥操作, 并且是岛屿; 所以我们默认桥 i 是连接岛屿i 和 i + 1之间的桥, 这样在做差分操作时就不会乱了;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m;
int t[N], p[N];
int lowbit(int x){
return x & -x;
}
int sum(int x){
int res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
res += t[i];
}
return res;
}
void add(int x, int u){
for(int i = x; i <= n; i += lowbit(i)){
t[i] += u;
}
}
signed main() {
IOS;
cin >> n >> m;
for(int i = 1; i <= m; i++) cin >> p[i];
int res = 0;
for(int i = 2; i <= m; i++){
int a = abs(p[i] - p[i - 1]);
int b = n - a;
if(a < b){
int l = min(p[i], p[i - 1]), r = max(p[i], p[i - 1]);
res += a;
int c = n - 2 * a;
add(l, c), add(r, -c);
}
else {
int l = min(p[i], p[i - 1]), r = max(p[i], p[i - 1]);
res += b;
int c = n - 2 * b;
add(1, c), add(l, -c);
add(r, c), add(n + 1, -c);
}
}
int minn = 9e18;
for(int i = 1; i <= n; i++){
minn = min(minn, sum(i));
}
cout << res + minn;
return 0;
}
E - Chords
难度: ⭐⭐⭐
题目大意
一个圆上均匀分布了2n个点, 现在有n条边把他们一一相连, 确保每个点有且只有一条边; 请问这n条边是否有相交的边;
解题思路
设边x的顶点是a1, a2; 边y的顶点是b1, b2; 如果x和y相交, 那么a1, a2, b1, b2一定是交替出现的; 通过这个规律我们可以用栈来模拟; 首先把每个点进行编号, 同一条边的两个顶点编号相同; 我们遍历所有点, 如果该点的编号和栈顶相同, 那么就把栈顶出栈, 否则就把该点的编号入栈;
如果a1, a2, b1, b2不是交替出现, 那么就a1a2b1b2, a1b1b2a2两种情况; 会发现这两种情况都会让栈变空; 所以如果最后栈空了, 就说明没有相交, 反之则存在相交;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 4e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m;
struct{
int l, r;
}a[N];
int p[N];
stack<int> s;
signed main() {
IOS;
cin >> n;
for(int i = 1; i <= n; i++){
int a, b;
cin >> a >> b;
p[a] = p[b] = i;
}
for(int i = 1; i <= 2 * n; i++){
if(s.size() && p[i] == s.top()){
s.pop();
}
else s.push(p[i]);
}
if(s.size()) cout << "Yes";
else cout << "No";
return 0;
}
F - Negative Traveling Salesman
难度: ⭐⭐⭐⭐
题目大意
给定一个带权有向图, 有n个点m条边, 边权可能为负数但不会有负环; 现在需要规划一条路线, 该路线可以经过所有点; 请问该路线的最小权值是多少; 对于一条边, 经过一次权值就要加一遍;
解题思路
由于n的范围只有20, 所以可以考虑状压dp; 设dp[S][i], 表示当前经过的情况是S, 且当前在点i; 状态转移就是看点i的上一个点是哪个; dp[S][i] = min(dp[S][i], dp[S - (1 << j)][j] + d[j][i]); 两点之间的最短距离可以用floyd来求;
!注意: 在状态转移过程中, 不存在的dp[S - (1 << j)][j]或者不存在d[j][i]时就不要转移了; 因为我们一般都初始化为无穷大inf, 但是边权可能是负数, 相加后结果就小于inf了, 最后拿inf判断是否存在时就会将其判为存在;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 4e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m;
int d[21][21];
int dp[1 << 20][21];
void floyd(){
for(int k = 1; k <= n; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
signed main() {
IOS;
cin >> n >> m;
for(int i = 1; i < 1 << n; i++){
for(int j = 1; j <= n; j++){
dp[i][j] = inf;
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
d[i][j] = inf;
}
}
for(int i = 1; i <= m; i++){
int a, b, c;
cin >> a >> b >> c;
d[a][b] = min(d[a][b], c);
}
floyd();
for(int i = 0; i < n; i++){
dp[1 << i][i + 1] = 0;
}
for(int i = 1; i < 1 << n; i++){
for(int j = 0; j < n; j++){
if((i >> j) & 1){
for(int k = 0; k < n; k++){
if((i - (1 << j)) >> k & 1 && d[k + 1][j + 1] != inf && dp[i - (1 << j)][k + 1] != inf){
dp[i][j + 1] = min(dp[i][j + 1], dp[i - (1 << j)][k + 1] + d[k + 1][j + 1]);
}
}
}
}
}
int res = inf;
for(int i = 1; i <= n; i++){
res = min(res, dp[(1 << n) - 1][i]);
}
if(res == inf) cout << "No";
else cout << res;
return 0;
}