C语言网_1454:[蓝桥杯][历届试题]蚂蚁感冒

题目 1454: [蓝桥杯][历届试题]蚂蚁感冒

时间限制: 1Sec 内存限制: 128MB

题目描述

长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。
每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。
当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。
这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

输入

第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。

接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数 据代表的蚂蚁感冒了。

输出

要求输出1个整数,表示最后感冒蚂蚁的数目。

样例输入

5
-10 8 -20 12 25

样例输出

3

题目分析

两只蚂蚁相遇各自反向可以看作是两个蚂蚁分别继续前进(下图来自于《挑战程序设计竞赛》第2版第一章例题Ants,题目稍有点不同,但相遇反向的概念是一样的),且所有蚂蚁前进的速度相同,不存在后面的蚂蚁追上前面的蚂蚁。
蚂蚁相遇反向可看做分别继续前进

解题思路一(模拟)

  1. 定义蚂蚁结构体,具有属性combine和cold来表示距离方向和是否感冒。
  2. 通过模拟,蚂蚁每前进一米就判断每只蚂蚁是否与其他蚂蚁相遇,直到全部走出杆子。
  3. 若两只蚂蚁相遇,只需处理感冒传染问题,而不需要转向。
  4. 全部走出杆子后,统计cold为true的蚂蚁。

AC代码一

#include <cstdio>
#include <cmath>

struct ant {
    int combine;    // 代表数值(方向和距离的结合)
    bool cold;     // true代表感冒,false代表未感冒
}ants[60];

 // 用于判断所有蚂蚁是否走出杆子
bool out(struct ant ants[], int n) {
    for (int i = 0; i < n; ++i) {
        // 若存在一只蚂蚁到竹竿左端的距离不是0或100
        if (ants[i].combine != 0 && ants[i].combine != 100) {
            return false;
        }
    }
    return true;
}

int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 0; i < n; ++i) {
            scanf("%d", &ants[i].combine);
            // 传进来的第一个参数所代表的蚂蚁患有感冒
            i == 0 ? ants[i].cold = true : ants[i].cold = false;
        }
        // 模拟一秒钟,每过一秒对蚂蚁是否全部out判断
        while (!out(ants, n)) {
            for (int i = 0; i < n; ++i) {
                // 对于没有走到最左端(距离为0)或最右端(距离为100)的蚂蚁
                if (ants[i].combine != 0 && ants[i].combine != 100) {
                    // 向左或向右前进combine都是加1
                    ants[i].combine++;
                }
            }
            // 两层循环来将蚂蚁两两对比
            for (int j = 0; j < n; ++j) {
                for (int i = j + 1; i < n; ++i) {
                    // 如果两只蚂蚁之间的距离(combine相加)小于等于1,且不是位于最左端的两只蚂蚁(combine不为0),则代表两只蚂蚁相遇
                    if (fabs((double) (ants[j].combine + ants[i].combine)) <= 1 && ants[j].combine && ants[i].combine) {
                        // 只需要对两只蚂蚁是否感冒进行判断,不需要考虑转向问题
                        if (ants[j].cold || ants[i].cold) {
                            ants[j].cold = true;
                            ants[i].cold = true;
                        }
                    }
                }
            }
        }
        // count对感冒的蚂蚁循环计数后输出
        int count = 0;
        for (int k = 0; k < n; ++k) {
            if (ants[k].cold) {
                count++;
            }
        }
        printf("%d\n", count);
    }
    return 0;
}

解题思路二(巧解)

既然两只蚂蚁相遇各自反向可以看做两只蚂蚁分别继续前进,
假设第一只蚂蚁右行,则它会感染其右边所有左行的蚂蚁,因为可看做其继续右行;
而被感染的第一只蚂蚁继续左行,感染它左边所有右行的蚂蚁。

故共存在以下两种情况:

  1. 若第一只蚂蚁右行,其右边所有左行蚂蚁感冒,且其左边所有右行的蚂蚁感冒;
  2. 若第一只蚂蚁左行,其左边所有右行蚂蚁感冒,且其右边所有左行的蚂蚁感冒。

所以只需要判断第一只蚂蚁的朝向,再统计两种情况下符合条件的蚂蚁个数即可。

AC代码二

#include <cstdio>
#include <cmath>

int main() {
    int n, ants[60];
    while (~scanf("%d", &n)) {
        for (int i = 0; i < n; ++i) {
            scanf("%d", &ants[i]);
        }
        int coldAnts = 1;
        int first = ants[0];
        for (int j = 1; j < n; ++j) {
            if (first > 0) {    // 如果第一只蚂蚁右行
                if (fabs(ants[j]) > first && ants[j] < 0) { // 第一只蚂蚁右边左行的蚂蚁全部感冒
                    coldAnts++;
                }
                if (fabs(ants[j]) < first && ants[j] > 0) { // 第一只蚂蚁左边右行的蚂蚁全部感冒
                    coldAnts++;
                }
            } else {    // 如果第一只蚂蚁左行
                if (fabs(ants[j]) < fabs(first) && ants[j] > 0) { // 第一只蚂蚁左边右行的蚂蚁全部感冒
                    coldAnts++;
                }
                if (fabs(ants[j]) > fabs(first) && ants[j] < 0) { // 第一只蚂蚁右边左行的蚂蚁全部感冒
                    coldAnts++;
                }
            }

        }
        printf("%d\n", coldAnts);
    }
    return 0;
}

经验总结

  1. 这道题一开始做的时候就按照模拟的思路,相遇转头,但是写完代码后提交WA了。
  2. 起初我判断相遇的条件是if (ants[j].combine + ants[i].combine == 0 && ants[j].combine && ants[i].combine)。后面发现我只考虑到蚂蚁在完成1m路程后,在整数距离相遇转头;而没有考虑蚂蚁在1s中1m的路程还走完,在非整点距离相遇转头。(如7 -8加1之后变为8 -7,过程中已经相遇了)。
  3. 考虑到这一点之后就比较难办了,判断相遇条件需要改成相加的绝对值小于1,但是转头后的距离会变成小数而不好控制(判断到达左端的条件以及全部走出杆子的条件都要改)。
  4. 后来看了一些大佬的思路,理解了之后再来看自己写的代码,发现只要忽略转向这个问题,把转向代码删掉再提交就AC了。
  5. 之后又顺着大佬的思路自己再写一遍代码,只能感慨自己的算法思路真实太逊了,遇到问题只会想着去模拟问题,而不能打破思维定式去推理出一些细节的点,emmm加油吧。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值