CSDN 编程竞赛三十期题解

竞赛总览

CSDN 编程竞赛三十期:比赛详情 (csdn.net)

竞赛题解

题目1、天然气订单

天然气运输成本昂贵,危险性高,为了节省运输成本,提倡绿色环保,需要尽可能的优化订单配送,比如相同地区的天然气订单可以一次性配送。现需要向多个地区运输天然气。但是同一个地区可能有多个订单需求。当前仅只知道某些成对的订单是同一个地区的,同一个地区的天然气需要尽可能一次性配送从而降低运输成本,所以需要尽可能地将同一个地区的订单放在一起。订单的编号是1到n。

#include <cstdio>
#include <vector>

int data [100005];

struct node {
    int nId;
    int cId;
};

int search (int x) {
    return data [x] == x ? x : data [x] = search (data [x]);
}

void input (node* p) {
    scanf ("%d", &p -> nId);
    p -> cId = search (p -> nId);
}

int main () {
    int n, m;
    scanf ("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) data [i] = i;
    for (int i = 1; i <= m; i++) {
        node x, y;
        input (&x);
        input (&y);
        data [(x.cId < y.cId ? y : x).cId] = (x.cId < y.cId ? x : y).cId;
    }
    std::vector<int> result [n + 1];
    for (int i = 1; i <= n; i++) {
        result [data [i] == i ? i : search (data [i])].push_back (i);
    }
    for (int i = 1; i <= n; i++) {
        if (i > 1 && result [i - 1].size () > 0) printf ("\n");
        for (unsigned int j = 0; j < result [i].size (); j++) {
            if (j > 0) printf (" ");
            printf ("%d", result [i][j]);
        }
    }
    return 0;
}

使用并查集算法,将有关联的订单归到一类。

相较传统并查集算法的题目而言,这道题多了一步统计的过程。需要将属于同一类别的订单放到对应列表中,最后集中输出即可。

因此,在归类过程中,如果同一类中有更小的订单号,可以用较小值表示该类型。例如,1 4 5是一类,2 3是一类,则分别可以用1和2代表这两类的订单。这样做可以为最后的统计操作提供便利。

题目2、小艺读书

书是人类进步的阶梯。小艺每周因为工作的原因会选择性的每天多读几页或者少读几页。小艺想知道一本n页的书她会在周几读完。

#include <cstdio>

int main () {
    int n; scanf ("%d", &n);
    int data [7]; for (int i = 0; i < 7; i++) scanf ("%d", &data [i]);
    int d; for (d = 0; n > 0; d = (d + 1) % 7) n = n - data [d];
    return 0;
}

直接暴力模拟,从周一开始,每次减少当天对应的页数,如果还有剩余页数继续跳到下一天,否则就停下来。这里有个优化点,可以一次性减去一周能看完的页数,直到剩余的页数不足一周的量时,再逐天判断。

题目3、买苹果

小易去附近的商店买苹果,奸诈的商贩使用了捆绑交易,只提供6个每袋和8个每袋的包装(包装不可拆分)。可是小易现在只想购买恰好n个苹果,小易想购买尽量少的袋数方便携带。如果不能购买恰好n个苹果,小易将不会购买。

int main () {
    int result = -1;
    int n;
    scanf ("%d", &n);
    int n_6 = n / 6 + 1;
    for (int i = 0; i < n_6; i++) {
        for (int j = 0; j < n_6; j++) {
            if (i * 6 + j * 8 == n) {
                if (i + j < result || result == -1) result = i + j;
            }
        }
    }
    return 0;
}

题目比较简单,直接一项一项去试就可以了。找到符合条件的情况时,再判断一下袋数是不是比之前的消耗量要小,如果更小就更新答案,直到找到最优解为止。

也可以使用贪心算法来求解,先判断一下要购买的苹果是否为偶数,如果不是偶数直接返回-1。

首先,假设全部买8个每袋的商品,不足的用6个每袋的商品补齐,确保买到的苹果数不少于需求量。

之后,减少8个每袋的商品数量,如果过程中买到的苹果数少于预期的话再增加6个每袋的商品。

直到找到恰好满足条件的解时,跳出循环。

这种方法理论可行,不过比赛时比较考验手速,博主并未尝试,只是提供一个思路,感兴趣的小伙伴可以自己试一下。

题目4、圆桌

有N个客人与足够多张的圆桌。主人安排每位客人坐在一个圆桌边,但是每位客人希望自己左右边上分别有一些空座位,不然会觉得害羞。注意,如果一个客人所在的圆桌只有他一个人,那么他左边的空座位数量就是他右边的空座位数量。试问主人需要准备多少个座位,才能让每个客人舒适的坐下。

#include <cstdio>
#include <algorithm>

long long int data1 [1000005];
long long int data2 [1000005];

int main () {
    long long int result = 0;
    long long int n;
    scanf ("%lld", &n);
    for (long long int i = 0; i < n; i++) scanf ("%lld %lld", &data1 [i], &data2 [i]);
    std::sort (data1, data1 + n);
    std::sort (data2, data2 + n);
    for (long long int i = 0; i < n; i++) result = result + max (data1 [i], data2 [i]);
    return 0;
}

贪心算法。尽量让空位能够拟合,计算之后还要加上n才能得到最终结果。

这道题在第11期竞赛时出现过一次。需要注意数据范围,必须用long long int才能全部通过,否则只能通过90%的测试数据。

猜你喜欢

转载自blog.csdn.net/x1051496412/article/details/129110610
今日推荐