坠落的蚂蚁

题目描述:
一根长度为1米的木棒上有若干只蚂蚁在爬动。它们的速度为每秒一厘米或静止不动,方向只有两种,向左或者向右。如果两只蚂蚁碰头,则它们立即交换速度并继续爬动。三只蚂蚁碰头,则两边的蚂蚁交换速度,中间的蚂蚁仍然静止。如果它们爬到了木棒的边缘(0或100厘米处)则会从木棒上坠落下去。在某一时刻蚂蚁的位置各不相同且均在整数厘米处(即1,2,3,…99厘米),有且只有一只蚂蚁A速度为0,其他蚂蚁均在向左或向右爬动。给出该时刻木棒上的所有蚂蚁位置和初始速度,找出蚂蚁A从此时刻到坠落所需要的时间。

输入描述:
第一行包含一个整数表示蚂蚁的个数N(2<=N<=99),之后共有N行,每一行描述一只蚂蚁的初始状态。每个初始状态由两个整数组成,中间用空格隔开,第一个数字表示初始位置厘米数P(1<=P<=99),第二个数字表示初始方向,-1表示向左,1表示向右,0表示静止。

输出描述:
蚂蚁A从开始到坠落的时间。若不会坠落,输出“Cannot fall!”

此题乍一看考的是模拟,但模拟起来细节又实在非常繁琐。便参考了前人Theramenes的巧妙思想,记录一下我自己的理解。

首先交换速度的含义不仅仅是交换速度的大小,同时也交换了方向。也就是说蚂蚁碰头后并不会越过对方,而是向反方向继续前进。
这就是最重要的相对位置永远不变,所谓不变指的并不是蚂蚁之间的距离不变,而是指没有谁能越过对方,所有蚂蚁只能在由左右两只蚂蚁组成的“囚笼”内移动。

搞清楚这点后,再讨论一下蚂蚁A的三种最终状态:未坠落(静止)、左边坠落、右边坠落。

  1. 未坠落
    由于任何时刻都只会有1只蚂蚁静止,那就是说如果蚂蚁A永不坠落,仅有的可能是它会是木棒上唯一的幸存者,又因为上面提到的“囚笼原理”,可知它左右两边的所有蚂蚁都会坠落。
  2. 向左坠落
    这个就更容易理解了,如果蚂蚁A在初始状态下,是自左向右数的第k只蚂蚁,那么由于“囚笼”,它左边的必定先于它坠落,则它会是第k只向左坠落的蚂蚁。
  3. 向右同理

现在已经明白了最终的状态,那么终极问题来了,如何确定蚂蚁A坠落的时间??换句话说,如果它向左坠落,如何确定第K只向左坠落的蚂蚁的时间呢?
刚才一直在以一个凡人的视角看这个问题,现在让我们体验一下上帝视角:

上帝可分不清每一只蚂蚁,更分不清它们是否调转了方向,他只会看到每个时刻上,每个位置上是否有蚂蚁。
也就是说在上帝的眼里,蚂蚁之间没有“囚笼”、它们可以互相穿越、它们从开始到结束,一直在向着一个方向前进,直到坠落。
视角不同,但看到的东西相同,殊途同归。

这样说来,第k只坠落于左端的蚂蚁走向坠落所用的时间,恰好是在初始时刻,第k只向左行走的蚂蚁,径直走到最左端所用的时间!!
真的很巧妙啊。

就好像向着喜欢的人走了九十九步,好不容易碰到了TA,却因为种种原因不得不掉头逃跑,殊不知在上帝的眼里,或是透过现象的本质里,却是你已经穿过TA,到达了终点。哈哈哈哈胡言乱语。

借鉴的代码:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
 
struct Ant
{
    int position;
    int direct;    //方向
    bool operator < (const Ant &a) const
    {
        return position<a.position;
    }
};
 
int main()
{
    int n;
    while(scanf("%d",&n) != EOF)
    {
        vector<Ant> ant(n);
        for(int i = 0; i<n; i++)
            scanf("%d %d",&ant[i].position,&ant[i].direct);
        sort(ant.begin(),ant.end());
        //接下来要做的就是找到静止的那只的位置,为此我们要先排序
        //这样找到的静止的蚂蚁左边有几只就出来了
        int target,toLeft = 0;    //这里选用向左走的为基准来做
        for(int i = 0; i<n; i++)    //遍历所有蚂蚁
        {
            if(ant[i].direct == 0)
                target = i;
            if(ant[i].direct == -1)
                toLeft++;
        }//现在的target就是静止的蚂蚁左边的数量了
        bool flag = false;
        int ans;
        if(toLeft == target)
            flag = true;
        else if (toLeft > target)//这样的话我们要找的就是所有向左走的蚂蚁中,第target蚂蚁
        {
            int cnt = 0;//计数器
            for(int i = 0; i<n; i++)
            {
                if(ant[i].direct == -1 && cnt == target)
                {
                    ans = ant[i].position;//这只蚂蚁的位置,即为它坠落所需的时间,也就是蚂蚁A坠落的时间
                    break;
                }
                else if(ant[i].direct == -1)
                    cnt++;
            }
        }
        else    //向左走的蚂蚁少,那么目标蚂蚁会向右落下
        {
            int cnt = 0;
            for(int i = n - 1; i>=0; i--)
            {
                if(ant[i].direct == 1 && cnt == n - target - 1)//相应的变化,cnt要变成静止蚂蚁右边的蚂蚁数量
                {
                    ans = 100 - ant[i].position; //由于是向右,就用100减一下
                    break;
                }
                else if(ant[i].direct == 1)
                    cnt++;
            }
        }
        if(flag)
            printf("Cannot fall!\n");
        else
            printf("%d\n",ans);
    }//while
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值