Codeforces Round #683 (Div. 2, by Meet IT)【ABCD】

比赛链接:https://codeforces.com/contest/1447

A. Add Candies

题意

\(1\)\(n\) 个袋子里依次有 \(1\)\(n\) 个糖果,可以选择操作 \(m\) 次,第 \(i\) 次操作可以:

  • 选择一个袋子,除了这个袋子外其他袋子都再放入 \(i\) 个糖果

问是否存在一种操作序列,使得最终 \(n\) 个袋子内的糖果数相同。

题解

将每个袋子内的糖果都加到 \(\frac{n(n + 1)}{2}\) 个。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        cout << n << "\n";
        for (int i = 1; i <= n; i++) cout << i << " \n"[i == n];
    }
    return 0;
}

B. Numbers Box

题意

给出一个 \(n \times m\) 的矩阵,每次操作可以:

  • 选择任意两个相邻元素各乘以 \(-1\)

可以操作任意次,问矩阵中所有元素之和的最大值为多少。

题解

有零的话可以把所有负数都变为正,答案即所有数的绝对值之和。
否则如果负数个数为奇数,需要减去一个绝对值最小的数。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        vector<int> a(n * m);
        int sum = 0, neg = 0, zero = 0, mi = INT_MAX;
        for (auto &x : a) cin >> x, sum += abs(x), neg += x < 0, zero += x == 0, mi = min(mi, abs(x));
        if (neg & 1 and zero == 0) sum -= 2 * mi;
        cout << sum << "\n";
    }
    return 0;
}

C. Knapsack

题意

有一个承重为 \(W\) 的背包,另有 \(n\) 个物品,分别重 \(w_i\) ,判断能否找到一些物品使得它们的总重 \(C\) 满足 \(\lceil \frac{W}{2} \rceil \le C \le W\) ,如果可以,输出这些物品的编号。

题解

首先去除 \(w_i > W\) 的元素,然后如果有单个 \(w_i \ge \lceil \frac{W}{2} \rceil\) 直接选取即可,此时余下的 \(w_i\) 均小于 \(\lceil \frac{W}{2} \rceil\) ,如果它们的总和小于 \(\lceil \frac{W}{2} \rceil\) 则无解,否则一定存在满足题意的序列,依次选取至不小于 \(\lceil \frac{W}{2} \rceil\) 即可。

证明

两个小于 \(\lceil \frac{W}{2} \rceil\) 的数相加有两种情况:

  • 小于 \(\lceil \frac{W}{2} \rceil\)
  • 不小于 \(\lceil \frac{W}{2} \rceil\) 且小于 \(W\)

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        long long n, W;
        cin >> n >> W;
        long long mid_W = (W + 1) / 2;
        vector<int> w(n);
        for (auto &x : w) cin >> x;
        vector<int> p(n);
        iota(p.begin(), p.end(), 0);
        sort(p.begin(), p.end(), [&](int x, int y) {
            return w[x] < w[y];
        });
        while (p.size() and w[p.back()] > W) {
            p.pop_back();
        }
        if (p.empty() or accumulate(p.begin(), p.end(), 0LL, [&](long long sum, int x) { return sum + w[x]; }) < mid_W) {
            cout << -1 << "\n";
            continue;
        }
        if (w[p.back()] >= mid_W) {
            cout << 1 << "\n" << p.back() + 1 << "\n";
            continue;
        }
        vector<int> v;
        long long sum = 0;
        for (auto i : p) {
            sum += w[i], v.push_back(i);
            if (sum > mid_W) break;
        }
        cout << v.size() << "\n";
        for (auto i : v) cout << i + 1 << " \n"[i == v.back()];
    }
    return 0;
}

D. Catching Cheaters

题意

给出两个字符串 \(s, t\) ,定义: \(f(s, t) = 4 \cdot LCS(s, t) - |s| - |t|\)
找出 \(s, t\) 所有连续子串匹配的可能情况中最大的函数值。

题解

\(dp_{ij}\) 的含义为以 \(s_i\)\(t_j\) 结尾的两个连续子串匹配的最大函数值。
状态转移方程分为两种情况:

  • \(s_i = t_j\)
    • 可以在以 \(s_{i - 1}\)\(t_{j - 1}\) 结尾的两个子串尾部各添加一个字符,即 \(dp_{ij} = dp_{i - 1 j - 1} + 2\)
    • 也可以在两个空串尾部添加一个字符,即 \(dp_{ij} = 0 + 2\)
  • \(s_i \ne t_j\)
    • 可以在以 \(s_{i - 1}\)\(t_{j - 1}\) 结尾的一个子串尾部添加一个字符,即 \(dp_{ij} = max(dp_{i - 1 j}, dp_{i j - 1}) - 1\)

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    string s, t;
    cin >> s >> t;
    vector<vector<int>> dp(n + 1, vector<int>(m + 1));
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (s[i - 1] == t[j - 1]) dp[i][j] = max(0, dp[i - 1][j - 1]) + 2;
            else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) - 1;
            ans = max(ans, dp[i][j]);
        }
    }
    cout << ans << "\n";
    return 0;
}
posted @ 2020-11-16 22:00  Kanoon  阅读(143)  评论(0编辑  收藏  举报