AtCoder Beginner Contest 364(A-F)题解

A - Glutton Takahashi


题意:

给你了N盘菜, 当你连续吃两盘甜的菜就死了,问能不能吃完所有的菜。当中间出现连续的两盘甜菜不行,最后两个位置可以出现连续的甜菜。

void solve(){
    int n;
    cin >> n;   
    vector<string > v;
    string s;
    for(int i = 1; i <= n; i++){
        cin >> s;
        v.push_back(s);
    }
    for(int i = 0; i < n - 2; i++){
        if(v[i] == "sweet" && v[i + 1] == "sweet"){
            cout <<"No" << endl;
            return ;
        }
    }
    cout <<"Yes" << endl;
}   

B - Grid Walk


题意

给你一张有边界的地图,地图中有若干障碍物,人物不能穿过障碍物和边界。给你人物的起始位置,和一个指令集,问经过这些指令后人物的位置。因为指令集不大,直接模拟即可。

void solve(){
    cin >> n >> m;
    cin >> sx >> sy;
    for(int i = 0; i <= n + 1; i++){
        for(int j = 0; j <= m + 1; j++){
            a[i][j] = '#';
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> a[i][j];
        }
    }
    string s; 
    cin >> s;
    int x = sx, y = sy;
    for(int i = 0; i < s.size(); i++){
        if(s[i] == 'L'){
            if(a[x][y - 1] != '#') y = y - 1;
        }else if(s[i] == 'R'){
            if(a[x][y + 1] != '#') y = y + 1;
        }else if(s[i] == 'U'){
            if(a[x - 1][y] != '#') x = x - 1;
        }else{
            if(a[x + 1][y] != '#') x = x + 1;
        }
    }
    cout << x <<" " << y << endl;
}

C - Minimum Glutton


题意

你有N盘菜肴,每盘菜肴有甜度和咸度两个属性。你可以将菜肴任意排序,按顺序吃掉,当甜度超过X或当咸度超过Y时,你死了。问:最少吃几盘菜。由于是最少吃几盘,最后不是被甜死就是咸死,按甜度和咸度排序,甜死的最小和咸死的最小取min即可。

void solve(){
    cin >> n >> X >> Y;
    for(int i = 1; i <= n; i++){
        cin >> a[i].first;
    }
    for(int i = 1; i <= n; i++){
        cin >> a[i].second;
    }
    int ans = n;
    sort(a + 1, a + 1 + n, cmp1);
    int sum1 = 0, sum2 = 0;
    int i = 0;
    while(i < n && sum1 <= X && sum2 <= Y) {
        i++;
        sum1 += a[i].first;
        sum2 += a[i].second;
    }
    ans = min(ans, i);
    i = 0, sum1 = 0, sum2 = 0;
    sort(a + 1, a + 1 + n, cmp2);
    while(i < n && sum1 <= X && sum2 <= Y) {
        i++;
        sum1 += a[i].first;
        sum2 += a[i].second;
    }
    ans = min(ans, i);
    cout << ans << endl;
}

D - K-th Nearest


题意

在一个数轴上已有N个点,接下来你要处理Q次询问。每次询问会给你一个点B,问距离这个点第K近的点距离它的距离。这题我们需要二分答案,假设答案为D,则[x - D, x + D]这个区间内必有>=K个点,距离变大,区间变大,区间内的点数不会变少,这样具有单调性的问题,可以使用二分去做。如何去计算[x - D, x + D] 这个区间内有多少点呢,我们依旧可以二分,找到第一个>= x - D
的下标p1和最后一个<= x + D的下标p2,点的个数即为p2 - p1 + 1了。

const int N = 1e5+5;
int a[N], n, q;

int Search1(int x){
    int l = 1, r = n;
    int ans = -1;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(a[mid] <= x) {
            ans = mid;
            l = mid + 1;
        }else r = mid - 1;
    }
    return ans;
}

int Search2(int x){
    int l = 1, r = n;
    int ans = -1;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(a[mid] >= x) {
            ans = mid;
            r = mid - 1;
        }else l = mid + 1;
    }
    return ans;
}


bool check(int b, int k, int d){
    int pos1 = Search1(b + d);
    int pos2 = Search2(b - d);
    if(pos1 == -1 || pos2 == -1) return 0;
    return (pos1 - pos2 + 1) >= k;
}
void solve(){
    cin >> n >> q;
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + 1 + n);
    int b, k;
    while(q--){
        cin >> b >> k; 
        int l = 0, r = 2e9;
        int ans = 0;
        while(l <= r){
            int mid = (l + r) / 2;
            if(check(b, k, mid)){
                ans = mid;
                r = mid - 1;
            }else l = mid + 1;
        }
        cout << ans << endl;
    }
}

E - Maximum Glutton


题意

问题和C题基本相同,不过现在求的是最多能吃几盘菜。这个题目给我的第一感觉是二维费用背包问题,求一个重量不超过X且体积不超过Y的背包最多能装多少物品。这个我们的dp设计为dp[i][j][k],考虑前i个物品,重量为j且体积为k的背包能装的最大物品数。时间复杂度为O(N * X * Y),由于X和Y都为1e4,显然是不可行的。由于N很小,考虑这个dp,dp[i][j][k]表示考虑前i个物品,恰好选择k个物品,甜度为k的最小咸度。这样时间复杂度为O(N * N * X) 。

const int N = 85;
const int M = 1e4+5;
typedef pair<int, int> PII;
PII a[N];
int n, X, Y;
int f[N][N][M];
void solve(){
    cin >> n >> X >> Y;
    for(int i = 1; i <= n; i++){
        cin >> a[i].first >> a[i].second;
    }
    memset(f, 0x3f, sizeof(f));
    f[0][0][0] = 0;
    for(int i = 1; i <= n; i++){
        f[i][0][0] = 0;
        for(int j = 1; j <= i; j++){
            for(int k = 0; k <= X; k++){
                f[i][j][k] = f[i - 1][j][k];
                if(k >= a[i].first) f[i][j][k] = min(f[i][j][k], f[i - 1][j - 1][k - a[i].first] + a[i].second);
            }
        }
    }
    int ans = 0;
    for(int i = 0; i <= n; i++){
        for(int j = 1; j <= X; j++){
            if(f[n][i][j] <= Y) ans = max(ans, i);
        }
    }
    ans = min(ans + 1, n);
    cout << ans << endl;
}

F - Range Connect MST


题意

给你一张有N + Q个点的图,有Q次操作,每次操作给你三个数,L,R,W。在第i次操作,我们会将Li-Ri的点与编号为N + i点连接起来。问:这个图是否连通,如果连通请输出这个图的最小生成树的值。根据最小生成树的Kruskal算法,我们总是先连接最小的边。我们先将操作按W的值进行升序排序,我们发现由于每次操作N + i 这个点总是不连通,即每个操作至少加一条边,且Li与N + i总是不连通。我们将Li与N + i连接后,与Li已连接的点都不需要与N+i进行连边了,我们可以跳过这些点,由于操作是连续的,与Li已连接的点也应该是连续的,所以我们跳过的点是一个区间。我们使用并查集来维护这些区间,每当我们与当前点建边后将这个点与加入Li的集合中,且跳到我们维护的这个点的右端点即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define ios ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define int long long
const int N = 2e5+5;
int ans[N];
int fa[N], fr[N];
struct node{
    int l, r, idx, w;
    bool operator < (const node &obj){
        return this->w < obj.w;
    }
};
int Find(int x){
    return fa[x] == x ? fa[x] : fa[x] = Find(fa[x]);
}
void init(){
    for(int i = 0; i < N; i++){
        fa[i] = i;
        fr[i] = i;
    }
}
void join(int a, int b){
    int aa = Find(a), bb = Find(b); 
    if(aa != bb){
        fa[aa] = bb;
        fr[aa] = fr[bb] = max(fr[aa], fr[bb]);
    }
}

vector<node> e;
int n, q;

void solve(){
    cin >> n >> q;
    int l, r, w;
    init();
    for(int i = 1; i <= q; i++){
        cin >> l >> r >> w;
        e.push_back({l, r, i, w});
    }
    sort(e.begin(), e.end());
    LL ans = 0;
    for(auto [u, v, idx, w] : e){
        ans += w;
        int p = fr[Find(u)] + 1;
        while(p <= v){
            ans += w;
            join(u, p);
            p = fr[Find(p)] + 1;
        }
    }
    if(fr[Find(1)] != n) {
        cout << -1 << endl;
        return ;
    }
    cout << ans << endl;
}

signed main(){
    ios; int _;
    _ = 1; //cin >> _;
    while(_--){
        solve();
    }
    return 0;
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值