【解题报告】Codeforces Round #375 (Div. 2)

本文提供了 Codeforces 723 赛事的五道题目(A-E)的详细解题思路及代码实现,涵盖枚举、字符串处理、贪心算法、图论与DFS、欧拉回路等算法技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


A. The New Year: Meeting Friends(Codeforces 723A)

思路

因为所求的坐标的范围比较小,因此直接枚举这个坐标,同时更新答案即可。

代码

#include <bits/stdc++.h>
using namespace std;

int a, b, c, res, ans;

int main() {
    scanf("%d%d%d", &a, &b, &c);
    ans = 400;
    for(int i = 1; i <= 100; i++) {
        res = abs(a - i) + abs(b - i) + abs(c - i);
        ans = min(res, ans);
    }
    printf("%d\n", ans);
    return 0;
}

B. Text Document Analysis(Codeforces 723B)

思路

首先将括号外的字符串和括号内的字符串分离开。方法是先定位所有括号,然后将所有括号中的字符串用 _ 字符连接起来放在一个新的字符串中,再用 _ 字符将原字符串中括号中的字符串(包括括号)屏蔽掉。
接着将两个字符串中非字母的字符改成空格,并将他们分别制作成字符串流,最后从字符串流中边输入边统计即可得到答案。

代码

#include <bits/stdc++.h>
using namespace std;

int n, idx, x, y;
string s, t;

int main() {
    ios_base::sync_with_stdio(false);
    cin >> n >> s;
    for(int i = 0; i < n; i++) {
        if(s[i] == '(') {
            idx = i;
        }
        if(s[i] == ')') {
            t = t + "_" + s.substr(idx + 1, i - idx - 1);
            for(int j = idx; j <= i; j++) {
                s[j] = '_';
            }
        }
    }
    for(int i = 0; i < s.size(); i++) {
        if(s[i] == '_' || s[i] == '(' || s[i] == ')') {
            s[i] = ' ';
        }
    }
    for(int i = 0; i < t.size(); i++) {
        if(t[i] == '_' || t[i] == '(' || t[i] == ')') {
            t[i] = ' ';
        }
    }
    stringstream in(t);
    stringstream out(s);
    while(out >> s) {
        x = max(x, (int)s.size());
    }
    while(in >> s) {
        y++;
    }
    cout << x << ' ' << y << endl;
    return 0;
}

C. Polycarp at the Radio(Codeforces 723C)

思路

首先肯定可以将所有不是编号 1m 乐队演奏的歌曲都改成 1m 的。那么最好的情况肯定是所有的歌曲都是 1m 乐队演奏的。为了让演奏曲目最少的乐队的演奏曲目最多,应该把 n 个曲目让 m 个乐队均摊演奏。那么每个乐队至少获得 n/m 首音乐的演奏机会。
接下来的事情就简单了,我们先枚举不是 1m 乐队演奏的音乐,将其改成 1m 乐队中演奏数目最少的乐队演奏的曲目。然后再枚举所有曲目,将演奏曲目超过 n/m 首的乐队的曲目分配给演奏数最少的乐队。
实现上,为了动态了解那个乐队的演奏曲目最少,可以用堆将每个乐队对曲目的“需求”维护起来(但是这题的数据量比较小,貌似暴力也行)。

代码

#include <bits/stdc++.h>
using namespace std;

typedef pair <int, int> p;
const int maxn = 2010;
int n, m, need, cnt, idx, dif, a[maxn], b[maxn];
priority_queue <p> pq;

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        if(a[i] <= m) {
            b[a[i]]++;
        }
    }
    need = n / m;
    for(int i = 1; i <= m; i++) {
        if(b[i] < need) {
            pq.push(p(need - b[i], i));
        }
    }
    for(int i = 1; i <= n; i++) {
        if(pq.empty()) {
            break;
        }
        if(a[i] > m) {
            p tmp = pq.top();
            pq.pop();
            idx = tmp.second;
            dif = tmp.first;
            a[i] = idx;
            b[idx]++;
            cnt++;
            if(dif > 1) {
                pq.push(p(dif - 1, idx));
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        if(pq.empty()) {
            break;
        }
        if(b[a[i]] > need) {
            p tmp = pq.top();
            pq.pop();
            idx = tmp.second;
            dif = tmp.first;
            b[a[i]]--;
            b[idx]++;
            a[i] = idx;
            cnt++;
            if(dif > 1) {
                pq.push(p(dif - 1, idx));
            }
        }
    }
    printf("%d %d\n", need, cnt);
    for(int i = 1; i <= n; i++) {
        printf("%d ", a[i]);
    }
    puts("");
    return 0;
}

D. Lakes in Berland(Codeforces 723D)

思路

先用 DFS 遍历图,找出所有的湖(注意临海的水域不算湖)。对于每个湖记录其中的一个坐标和其面积。然后根据面积对湖排序,找到面积最小的几个湖,用 DFS 的方法从之前记录的坐标开始填湖。最后输出图即可。

代码

#include <bits/stdc++.h>
using namespace std;

// 将与湖有关的信息封装起来以便排序
struct lake {
    int x, y, area;
    lake() {}
    lake(int x, int y, int area): x(x), y(y), area(area) {}
    bool operator < (const lake& o) const {
        return area < o.area;
    }
};

const int maxn = 60;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
bool vis[maxn][maxn];
char G[maxn][maxn];
int n, m, k, res, ans, del;
vector <lake> v;

// 在图中找连通分量(湖)
int dfs(int x, int y) {
    vis[x][y] = true;
    // 如果临海的话就返回-1
    if(G[x][y] == 0) {
        return -1;
    }
    int res = 0;
    bool ok = true;
    // 向四个相邻的方向走
    for(int i = 0; i < 4; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(G[nx][ny] == '*' || vis[nx][ny]) {
            continue;
        }
        int t = dfs(nx, ny);
        // 如果发现临海就标记一下
        if(t == -1) {
            ok = false;
        }
        res += t;
    }
    return ok ? res + 1 : -1;
}

// 在图中填湖
void land(int x, int y) {
    G[x][y] = '*';
    for(int i = 0; i < 4; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(G[nx][ny] != '*') {
            land(nx, ny);
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++) {
        scanf("%s", G[i] + 1);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            if(G[i][j] == '*' || vis[i][j]) {
                continue;
            }
            res = dfs(i, j);
            if(res == -1) {
                continue;
            }
            v.push_back(lake(i, j, res));
        }
    }
    del = v.size() - k;
    // 按照面积对湖排序
    sort(v.begin(), v.end());
    for(int i = 0; i < del; i++) {
        ans += v[i].area;
        land(v[i].x, v[i].y);
    }
    printf("%d\n", ans);
    for(int i = 1; i <= n; i++) {
        printf("%s\n", G[i] + 1);
    }
    puts("");
    return 0;
}

E. One-Way Reform(Codeforces 723E)

思路

首先显然每个偶度数的点都能够达到入度等于出度的状态。奇度数的点则必然不行。但是奇度数的点可以配合偶度数的点使其达到入度等于出度的状态。
这样的状态让我们想起欧拉回路。
正好对于每个连通分量奇度数的点的数量都是偶数。那么我们对奇度数的点之间建立无向边。这样原图就变成能够构成欧拉回路的图(正好各个连通分量之间也连通了)。实现起来是设置一个虚拟节点 0 ,让所有奇度数的点向这个点连无向边。
最后在用 DFS 访问欧拉回路的同时输出访问的边的方向即可(注意要标记已经访问过的边)。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 205;
bool vis[maxn][maxn];
int t, n, m, u, v, ans;
vector <int> G[maxn];

void dfs(int u) {
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if(vis[u][v]) {
            continue;
        }
        vis[u][v] = vis[v][u] = true;
        if(u > 0 && v > 0) {
            printf("%d %d\n", u, v);
        }
        dfs(v);
    }
}

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 0; i <= n; i++) {
            G[i].clear();
        }
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        ans = 0;
        for(int i = 1; i <= n; i++) {
            if(G[i].size() & 1) {
                G[0].push_back(i);
                G[i].push_back(0);
            }
            else {
                ans++;
            }
        }
        printf("%d\n", ans);
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++) {
            dfs(i);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值