Codeforces Round 888 (Div. 3)

本文介绍了CodeforcesRound888(Div.3)中的五道编程竞赛题目,包括题目大意、解题思路和代码实现。涉及算法包括基于高度差的交谈计数、数组的奇偶性排序、满足特定条件的路径寻找、前缀和的排列组合以及药水合成的最小花费计算。每个问题都通过分析并提出有效的解决方案,展示了如何运用编程技巧解决竞赛中的问题。
摘要由CSDN通过智能技术生成

目录

A. Escalator Conversations

B. Parity Sort

C. Tiles Comeback

D. Prefix Permutation Sums

E. Nastya and Potions


比赛链接:

Dashboard - Codeforces Round 888 (Div. 3) - Codeforces

A. Escalator Conversations

题目大意:有m层楼梯,每两个相邻的层与层之间的高度是k,弗拉德的身高是H,总共有n个人,第i个人的身高是hi (1 <= i <= n),当弗拉德与其他人身高相同的时候才能与其他人交谈,弗拉德站在台阶上能弥补一部分身高的差距使得能与其他人交谈,问弗拉德总共能与几人交谈

思路:记录每两个阶梯之间的高度差,依次遍历每个人的身高,若存在一个人的身高与弗拉德的身高之差的绝对值与某两个阶梯之间的高度差相等,则结果值加一

void solve() {
    int i, j, k, n, m, h;
    cin >> n >> m >> k >> h;
    vector<int>nums(n, 0);
    for (i = 0; i < n; i++) {
        cin >> nums[i];
    }
    set<int>st;
    for (i = 2; i <= m; i++) {
        st.insert((i - 1) * k);
    }
    ll ans = 0;
    for (i = 0; i < n; i++) {
        int item = abs(nums[i] - h);
        if (st.count(item)) ans++;
    }
    cout << ans << endl;
}

B. Parity Sort

题目大意:有一个长度为n的数组a,有一个操作是可以交换数组a里面的元素ai和aj(i != j),并且ai和aj要么同为偶数要么同为奇数,问是否可以通过不断的交换使得数组a呈现非递减排序

思路:因为只有奇数能和奇数交换位置,偶数和偶数交换位置,所以我们可以再开一个数组b用来保存数组a的值,并且对其进行排序,如果最终能达到数组a递增排序的话,则a[i]和b[i]应该同为奇数或偶数,否则最终无法达成非递减排序

void solve() {
    int i, j, k, n, m, h;
    cin >> n;
    vector<int>nums(n, 0);
    for (i = 0; i < n; i++) {
        cin >> nums[i];
    }
    vector<int>item(nums);
    sort(nums.begin(), nums.end());
    bool flag = true;
    for (i = 0; i < n; i++) {
        if (nums[i] % 2 == 0 && item[i] % 2 != 0 || nums[i] % 2 != 0 && item[i] % 2 == 0) flag = false;
    }
    if (!flag) cout << "NO" << endl;
    else cout << "YES" << endl;
}

C. Tiles Comeback

题目大意:有n个格子和一个整数k,第i个格子被染上一个颜色ci,问是否能够找到一条从格子1到格子n的路径,使得:

1、p(路径的总长)能够被k整除

2、路径能分成长度为k的块

3、每个块中的颜色相同,而块与块之间的颜色不同

思路:总共可以分成两种情况,一种是首尾颜色相同,另一种是首尾颜色不同

情况1则需要统计首尾所占有的颜色是否≥k即可,而情况2则需要分别统计首颜色和尾颜色的数量,然后存在一个下标index,使得1~index之间的颜色首的数目大于等于k且index~n之间的颜色尾的数目大于等于k

对于为什么用k来衡量,证明如下:因为题干要求路径总长度必须是k的倍数,并且每一个小块的长度也必须k,所以如果存在一条路径p的总长是k的倍数,并且能分成大于2的块的个数(3,4,5...),那么也一定能分成比此刻分的块更小的块数,而能分成更小的块数不一定能分成块更多的块数,即分成小的块数->分成块多的块数为一个必要不充分条件,所以我们用最小的来写,也就是当有最多两个块的个数刚好≥k时,我们可以令这两个块取k个数,则p的总长为2 * k,条件成立

void solve() {
    int i, j, k, n;
    cin >> n >> k;
    vector<int>nums(n, 0), item;
    map<int, int>mp;
    int count1 = 0, count2 = 0;
    int index1, index2;
    for (i = 0; i < n; i++) {
        cin >> nums[i];
        mp[nums[i]]++;
    }
    for (index1 = 0; index1 < n; index1++) {
        if (nums[index1] == nums[0]) {
            count1++;
        }
        if (count1 == k) break;
    }
    for (index2 = n - 1; index2 >= 0; index2--) {
        if (nums[index2] == nums[n - 1]) {
            count2++;
        }
        if (count2 == k) break;
    }
    bool flag = false;
    if (nums[0] == nums[n - 1] && mp[nums[0]] >= k) flag = true;
    if (index1 < index2) flag = true;
    if (flag) cout << "YES" << endl;
    else cout << "NO" << endl;
}

D. Prefix Permutation Sums

题目大意:给你一个前缀和数组,但是是缺少了一个数的前缀和数组,问此缺少了一个数的前缀和数组是否能组成一个排列

思路:总共有三种情况

情况一:缺少的为最左面的数

情况二:缺少的为最右面的数

情况三:缺少的为中间的任意一个数

我们可以对此前缀和数组进行差分处理,统计每一个差分后所出现的数,然后再统计哪些数未出现,如果是情况一,则最终未出现的两个数之和即为此数组最左面的数,如果是情况二,那么缺少的数只能是一个,如果是第三种情况,则缺少的任意一个数与旁边两数之差的和即为该旁边两数之差,即如果数组为a, b, c,缺少b,则对其做差分(b - a) + (c - b) = c - a,所以缺少的两个数的和即为该旁边两数之差,情况三只能是缺少两个数,若缺少的为其他数则错误,当n=2时对数组进行不了差分,所以特判一下即可,随后对其进行模拟即可

void solve() {
    ll i, j, k, n;
    map<ll, ll>mp;
    cin >> n;
    vector<ll>nums(n - 1, 0);
    for (i = 0; i < n - 1; i++) {
        cin >> nums[i];
    }
    if (n == 2) {
        if (nums[0] == 1 || nums[0] == 2 || nums[0] == 3) cout << "YES" << endl;
        else cout << "NO" << endl;
        return;
    }
    for (i = n - 2; i >= 1; i--) {
        ll item = nums[i] - nums[i - 1];
        mp[item]++;
    }
    set<int>st;
    ll ans = 0;
    for (i = 1; i <= n; i++) {
        if (mp[i] == 0) {
            ans += i;
            st.insert(i);
        }
    }
    if (st.size() == 2 && ans == nums[0]) {
        cout << "YES" << endl;
        return;
    } else if (st.count(nums[0])) {
        ans -= nums[0];
        mp[nums[0]]++;
        st.erase(st.lower_bound(nums[0]));
    }
    if (st.size() == 1) {
        cout << "YES" << endl;
    } else {
        if (st.size() == 2 && ans >= 1 && ans <= n && mp[ans] >= 2) cout << "YES" << endl;
        else if (st.size() == 2 && ans > n && mp[ans] >= 1) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
}

E. Nastya and Potions

题目大意:有n个药水,t个不花钱的药水,分别给你这n个药水的价格ci(1 <= i <= n),和合成第i种药水的方案,问合成每个药水的最小花费是多少

思路:对每种药水所需要合成的药水进行建图,然后dfs依次进行遍历,将合成每种药水按合成方案的药水价格来计算一个价格总和p,然后和自己本身的价格取最小值即可,如果没有该药水的合成方案或者该药水的价格本来就是0则返回对应的cost值即可,因为题干有说药水a合成药水b后药水b就不能合成药水a,所以不存在环的情况


更新:刚看了一眼估计fst了,第14个测试点超时了,思路没问题,更新后的代码如下

vector<ll>cost;
vector<bool>visit;
vector<vector<int>>G;

void dfs(int u) {
    if (G[u].size() == 0 || cost[u] == 0) {
        visit[u] = true;
        return;
    }
    ll p = 0;
    for (int i = 0; i < G[u].size(); i++) 
        if (!visit[G[u][i]]) {
            dfs(G[u][i]);
            p += cost[G[u][i]];
        } else p += cost[G[u][i]];
    visit[u] = true;
    cost[u] = min(p, cost[u]);
}
void solve() {
    G.clear();
    cost.clear();
    visit.clear();
    int i, j, k, n;
    cin >> n >> k;
    cost.resize(n + 1, 0);
    visit.resize(n + 1, false);
    G.resize(n + 1);
    for (i = 1; i <= n; i++) cin >> cost[i];
    for (i = 0; i < k; i++) {
        int x;
        cin >> x;
        cost[x] = 0;
    }
    for (i = 1; i <= n; i++) {
        int m, x;
        cin >> m;
        for (j = 0; j < m; j++) {
            cin >> x;
            G[i].push_back(x);
        }
    }
    for (i = 1; i <= n; i++) {
        if (!visit[i]) dfs(i);
    }
    for (i = 1; i <= n; i++) cout << cost[i] << " ";
    cout << endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值