Loading

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;   
}
posted @ 2024-01-31 00:35  mostimali  阅读(66)  评论(0编辑  收藏  举报