• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【贪心+构造】codeforces 2209 D. Ghostfires

题目

https://codeforces.com/contest/2209/problem/D

题意

第一行输入一个正整数 \(T(1 \leq T \leq 10^4)\),每个测试用例给定三个整数 \(r,g,b(0 \leq r,g,b \leq 10^6,r+g+b > 0)\),代表有 \(r\) 个字母 R,\(g\) 个字母 G,\(b\) 个字母 B。问:你最多能构造多长的字符串,满足任何相邻位置字母不同,且相隔距离为 \(3\) 的也不同(比如 \(RGBR,RR\) 都不满足),输出任意一个最长的字符串。

题解

对三种元素的数量进行从高到低排序,不妨将排序后的元素记为 \(A,B,C\) 数量分别为 \(a, b, c\)。

若数量最多的元素 \(A\) 的数量 \(a\) 大于等于其它两种元素 \(B,C\) 的数量 \(b,c\) 之和,则可以按以下方式进行构造:

  • 若只有一种元素存在,即 \(b==0\),则直接输出一个元素 \(A\);
  • 否则先重复输出 \(AB\),直至 \(B\) 的数量 \(b\) 用尽,随后交替输出 \(A\) 和 \(C\),直到有元素的数量用尽。

若数量最多的元素 \(A\) 的数量 \(a\) 小于其它两种元素 \(B,C\) 的数量 \(b,c\) 之和,则可以按以下方式进行构造:

  • 首先以二分法计算出 \(num\),使得 \(a - num == b + c - num * 2\) 能够成立
    随后遍历所有三元组为开头的情况,由于只有 \(3\) 种不同的元素,所以其实只有 \(9\) 种不同的三元组(指使用三个元素构成一组,组内元素各不相同)
    当输出完三元组后,就可以按照 \(ABAB...ACAC...、BABA...CACA...或CACA...BABA...\) 的形式构造出答案,开头的元素取决于已经构造的字符串倒数第二个元素是哪个字符。

参考代码

#include<bits/stdc++.h>
using namespace std;
constexpr int N = 3;
constexpr string SERIALS[2] = {"RGBGBRBRG", "RBGBGRGRB"};
int T = 1;
int a[N], b[N], idx[N];

void solve() {
    string ans;
    auto &S = SERIALS[0];
    for (int i = 0; i < 3; ++ i) cin >> a[i], idx[i] = i;
    sort(idx, idx + 3, [&](int i, int j) {
        return a[i] > a[j];
    });
    if (a[idx[1]] == 0) {// 只有一种字符,那么最长的字符串只有一个字符
        cout << S[idx[0]] << '\n';
        return ;
    }
    if (a[idx[0]] >= a[idx[1]] + a[idx[2]]) {// 数量最多的字符大于等于其它两种字符加起来的数量
        // 这种情况一定是 ABABAB...ACAC...(A) 的形式
        // 其中 A 是数量最多的字符,B、C是另外两种字符
        while (a[idx[1]] -- && a[idx[0]] --) cout << S[idx[0]] << S[idx[1]];
        while (a[idx[0]] --) {
            cout << S[idx[0]];
            if (a[idx[2]] --) cout << S[idx[2]];
            else break;
        }
        cout << '\n';
        return ;
    }
    // 求一元一次方程 a[idx[0]] - num = a[idx[1]] + a[idx[2]] - 2 * num
    int num = a[idx[1]] + a[idx[2]] - a[idx[0]];
    for (int i = 0; i < 3; ++ i) a[i] -= num;// 每种元素的个数要扣去已经组成三个元素为一组的组数
    memcpy(b, a, sizeof a);// 备份数组 a
    for (auto &it: SERIALS) {// 遍历每种三个元素为一种的可能性
        for (int i = 0; i < 3; ++ i) {// 遍历出所有三元组为开头的情况
            memcpy(a, b, sizeof b);// 还原数组 a
            string res;
            // 以字符串 it 的下标 k 为起始点,连续放入 num 个三元组 
            // 当遍历到结尾的时候,还要遍历下一个位置就回到最左侧的下标位置
            for (int j = 0, k = i * 3; j < num; ++ j, k = (k + 3) % 9) {
                res.push_back(it[k]);
                res.push_back(it[k + 1]);
                res.push_back(it[k + 2]);
            }
            // 若存在三元组,且已构造的字符串倒数第二个字符不是初始时数量最多的那种字符
            // 则可以将初始时数量最多的字符视为 A,其它两种字符视为 B、C,构造出 ABABAB...ACAC...
            if (!res.empty() && res[res.size() - 2] == S[idx[0]]) {
                for (int p = 1; p <= 2; ++ p) {
                    while (a[idx[0]] > 0 && a[idx[p]] > 0) {
                        res.push_back(S[idx[0]]);
                        res.push_back(S[idx[p]]);
                        -- a[idx[0]], -- a[idx[p]];
                    }
                }
            } else {
                // 首先用 pos 维护出倒数第二个元素
                int pos = idx[1];
                if (!res.empty()) {// 已经存在构造的字符串
                    for (int p : idx) {
                        if (S[p] == res[res.size() - 2]) {// 跟已构造的字符串倒数第二个元素相同
                            pos = p;
                            break;
                        }
                    }
                }
                if (res.empty() || (S[pos] != res.back() && S[pos] != res[res.size() - 3])) {// 放入新的元素不会造成冲突
                    while (a[pos] > 0 && a[idx[0]] > 0) {
                        res.push_back(S[pos]);
                        res.push_back(S[idx[0]]);
                        -- a[pos], -- a[idx[0]];
                    }
                    for (int i = 0; i < 3; ++ i) {
                        if (i != pos && i != idx[0]) {
                            if (res.empty() || (S[i] != res.back() && S[i] != res[res.size() - 3])) {// 放入新的元素不会造成冲突
                                while (a[i] > 0 && a[idx[0]] > 0) {
                                    res.push_back(S[i]);
                                    res.push_back(S[idx[0]]);
                                    -- a[i], -- a[idx[0]];
                                }
                            }
                            break;
                        }
                    }
                }
            }
            if (ans.size() < res.size()) ans = res;// 若答案更优,则更新答案
        }
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

posted on 2026-03-29 15:34  RomanLin  阅读(2)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3