AtCoder ABC 369题解

前言

本题解部分思路来源于网络,仅供参考

A - 369

题目大意

给定 \(A\)\(B\) 两个整数,求有多少个整数 \(x\) 使得

  • 可以通过某种排列使得 \(A\)\(B\)\(x\) 为等差数列。

解题思路

稍加分析即可得到:

  • 如果 \(A = B\) 则结果为 \(1\)

  • 如果 \(A = B\)\((A + B) \bmod 2 = 1\) 则结果为 \(2\)

  • 否则,结果为 \(3\)

code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    if (a == b) printf("1\n");
    else if ((a + b) % 2 == 1) printf("2\n");
    else printf("3\n");
    return 0;
}

B - Piano 3

题目大意

高桥有一架有 \(100\) 个键的钢琴,给定一个操作序列,按如下操作计算出操作完成后的疲劳程度。

  • 当手从 \(a\) 移动到 \(b\) 时,疲劳程度增加 \(\left| a-b \right|\)

解题思路

根据题目模拟即可。

code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int flag1 = 1, flag2 = 1, ll, lr, ans = 0;
    int a, n;
    char c;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d %c", &a, &c);
        if (c == 'L') {
            if (flag1) {
                ll = a;
                flag1 = 0;
            } else {
                ans += abs(a - ll);
                ll = a;
            }
        } else {
            if (flag2) {
                lr = a;
                flag2 = 0;
            } else {
                ans += abs(a - lr);
                lr = a;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

C - Count Arithmetic Subarrays

题目大意

给定长度为 \(N\) 的数列 \(\left(A_1,A_2,...\ ,A_N\right)\) ,求有多少个数对 \((l,r)\quad1 \leqslant l \leqslant r \leqslant N\) 使得数列 \((A_l,A_{l + 1},...\ ,A_r)\) 为等差数列。

解题思路

可以通过遍历一遍数组,查找每个等差数列,然后组合计数。具体见代码。

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
    int n;
    int ans = 0, la, d = 0x3f3f3f3f, a, cnt = 1;
    scanf("%lld%lld", &n, &la);
    for (int i = 2; i <= n; i++) {
        scanf("%lld", &a);
        if (a - la != d) {
            ans += cnt * (cnt - 1) / 2 + cnt - 1;
            cnt = 2;
            d = a - la;
        } else {
            cnt++;
        }
        la = a;
    }
    ans += cnt * (cnt - 1) / 2 + cnt;
    printf("%lld\n", ans);
    return 0;
}

D - Bonus EXP

题目大意

高桥打怪,如果这是打的第偶数个怪物,则获得双倍积分,否则只获得一份积分。放走怪物不获得积分。求如何获得最大积分?

解题思路

题目可以整理成如下图:

\(i\) 列节点(从 \(0\) 开始标号)代表在杀死或放走第 \(i\) 只怪物后获得的积分总数。第 \(1/2\) 行节点代表 \(1/2\) 倍的积分。只要填好每一个节点上的数,就可以求出答案。

code

#include <bits/stdc++.h>
using namespace std;
long long dp[200005][2];
signed main() {
    int n;
    scanf("%d", &n);
    dp[0][1] = -0x3f3f3f3f;
    for (int i = 1, a; i <= n; i++) {
        scanf("%d", &a);
        for (int j = 0; j <= 1; j++) {
            dp[i][j] = max(dp[i][j], dp[i - 1][j]);
            dp[i][1 - j] = max(dp[i][1 - j],
                               dp[i - 1][j] + a * (j + 1));
        }
    }
    printf("%lld\n", max(dp[n][0], dp[n][1]));
    return 0;
}

E - Sightseeing Tour

题目大意

给定一个图,想从 \(1\) 节点走到 \(N\) 节点,但是有几条路径必须经过,求最短路径。

解题思路

可以先对这个图进行 Floyed 求出两个节点之间的关系,然后枚举每一条钦定路径的顺序,方向,求出每一种方案的最短路径,最后输出长度最短的结果。

code

#include <bits/stdc++.h>
#include <climits>
#include <cstdio>
using namespace std;
using ll = long long;
ll dis[405][405];
ll u[200005], v[200005], w[200005];
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    memset(dis, 0x3f, sizeof(dis));
    for (int i = 1; i <= n; i++) {
        dis[i][i] = 0;
    }
    for (int i = 1; i <= m; i++) {
        scanf("%lld%lld%lld", &u[i], &v[i], &w[i]);
        dis[v[i]][u[i]] = dis[u[i]][v[i]] =
            min(dis[u[i]][v[i]], w[i]);
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                dis[i][j] =
                    min(dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
    int q;
    scanf("%d", &q);
    while (q--) {
        int k, id[6];
        scanf("%d", &k);
        for (int i = 1; i <= k; i++) {
            scanf("%d", id + i);
        }
        sort(id + 1, id + 1 + k);
        ll ans = LLONG_MAX;
        do {
            for (int j = 0; j < (1 << k); j++) {
                ll cur = 0;
                for (int i = 1; i <= k; i++) {
                    cur += w[id[i]];
                }
                for (int i = 1; i <= k + 1; i++) {
                    cur += dis[i == 1 ? 1
                               : (j >> (i - 2)) & 1
                                   ? v[id[i - 1]]
                                   : u[id[i - 1]]]
                              [i > k ? n
                               : (j >> (i - 1)) & 1
                                   ? u[id[i]]
                                   : v[id[i]]];
                }
                ans = min(ans, cur);
            }
        } while (next_permutation(id + 1, id + 1 + k));
        printf("%lld\n", ans);
    }
    return 0;
}

F - Gather Coins

题目大意

给定一个表格,表格上有一些金币,经过有金币的格子会自动拾取金币,而且只能向下的向右行走,求怎么走可以使拾取的金币最多,并输出最多可以拾取的金币数量。

解题思路

这道题是一道动态规划,令 \(dp_i\) 为遇到第 \(i\) 个金币时,最大拾取的金币个数。

因为第 \(i - 1\) 个金币在第 \(i\) 个金币的左上方,所以可以把所有金币按横轴排序,至于纵轴,可以通过树状数组维护。有了第 \(i\) 个节点左上方的金币个数,则动态转移方程为:

\[dp_i = \max \limits_{\substack{x_j \leqslant x_i \\ y_j \leqslant x_i}} \left\{dp_j\right\} \]

code

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
using PII = pair<int, int>;
int h, w, n;
PII p[200005], dp[200005];
PII f[200005];
void modify(PII x, int pos) {
    while (pos <= w) {
        f[pos] = max(f[pos], x);
        pos += pos & -pos;
    }
}
PII query(int pos) {
    PII res{-1, 0};
    while (pos) {
        res = max(res, f[pos]);
        pos -= pos & -pos;
    }
    return res;
}
int main() {
    scanf("%d%d%d", &h, &w, &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &p[i].fi, &p[i].se);
    p[0] = {1, 1};
    p[n + 1] = {h, w};
    sort(p + 1, p + 1 + n);
    for (int i = 1; i <= n; i++) {
        f[i] = {INT_MIN, 0};
    }
    modify(dp[0], 1);
    for (int i = 1; i <= n + 1; i++) {
        dp[i] = query(p[i].se);
        modify({++dp[i].fi, i}, p[i].se);
    }
    printf("%d\n", dp[n + 1].fi - 1);
    string ans;
    for (int i = n + 1; i; i = dp[i].se) {
        for (int j = 1; j <= p[i].fi - p[dp[i].se].fi; j++)
            ans += 'D';
        for (int j = 1; j <= p[i].se - p[dp[i].se].se; j++)
            ans += 'R';
    }
    reverse(ans.begin(), ans.end());
    cout << ans << endl;
    return 0;
}
posted @ 2024-09-03 21:19  sxl701817  阅读(143)  评论(0)    收藏  举报