欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

牛客--坠落的蚂蚁

程序员文章站 2024-03-18 22:38:22
...

坠落的蚂蚁

链接:https://www.nowcoder.com/questionTerminal/fdd6698014c340178a8b1f28ea5fadf8
来源:牛客网

一根长度为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!”

牛客--坠落的蚂蚁

两篇题解链接:题解一题解二

题解一

本题最重要的逻辑是蚂蚁的相对位置永远不变!!这个逻辑直接推导出了本题的解法之一
首先,我们来确定怎么判断蚂蚁不会坠落,有两种情况————
第一种:静止的蚂蚁两边的蚂蚁都不会碰到这只蚂蚁,也就是说,左边的往左走,右边的往右走
第二种:蚂蚁的右边有向左走的,左边有向右走的,按照一般的理解一开始静止的蚂蚁一定
是会掉下去的,但是注意一开始提到的那个逻辑,蚂蚁的相对位置不变,并且移动方向也不变!
什么意思呢,比如整个树枝上向左走有n个,向右走有m个。那么在任何时间向左走和向右走的
数量都是n和m,这时候结合蚂蚁的相对位置,在无限的时刻,向左走的n只蚂蚁都掉下了树枝,
这n只不一定都是原来初始状态向左走的,但一定是一开始左边的n只蚂蚁,因为相对位置不变。
同理,右边m只也都掉出去了,那么如果n==m,并且静止的蚂蚁左右都有n(m)只。那么,在某个时刻,
左边n只无论之前是向哪里走的,一定都下去了。
所以,我们把结论推广,只要静止的蚂蚁左边的蚂蚁数量,等于所有蚂蚁中往左走的数量,
亦或者右边的等于向右走的那么它就不会掉下去。

那么,怎么判断蚂蚁什么时候下去呢
这时候肯定能确定这只蚂蚁左右数量不等了。接下来就是很巧妙的思想了,如果该蚂蚁
左边的蚂蚁数量小于向左走的蚂蚁数量,那么它总会加入向左走的大军最后掉落。这时候
我们宏观的去看,我们定位所有在向左走的蚂蚁,并且定位静止的那只蚂蚁的位置,并且
标记为k(第k个蚂蚁),这时开始移动,我们看不到蚂蚁之间交换速度,我们只知道他们
像是穿过对方继续往下走。让蚂蚁继续走,直到某一刻我们观察到第k只向左走的蚂蚁
掉下去了,暂停。现在考虑所有蚂蚁的相对位置不变!如果是第k个向左走的蚂蚁下去了
那么他之前的向左走的蚂蚁都下去了,反映到相对位置上来说,就是树枝上左边k-1只都下去了,
那么这一瞬间掉下去的想必就是相对位置在第k的蚂蚁了————也就是原来静止的那只。
也就是说一开始所有向左走的蚂蚁中,第k个蚂蚁要走多远,就是最终答案!!
同样,如果反过来,右边的少于向右走的,也一样。
相关代码

#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;
                    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;
                    break;
                }
                else if(ant[i].direct == 1)
                    cnt++;
            }
        }
        if(flag)
            printf("Cannot fall!\n");
        else
            printf("%d\n",ans);
    }//while
    return 0;
}//main

题解二:

题目看起来很复杂而且要模拟的话也比较麻烦,但是如果仔细观察的话就会发现这样一个事实:两只非A(A蚂蚁是刚开始静止的那只蚂蚁)蚂蚁相碰的时候交换各自的速度其实相当于两只蚂蚁还是各走各的路,没有碰头,速度不变。
然后假设蚂蚁A的位置是pos
基于上面的结论
那么在pos左边并且初速度向左-1的蚂蚁 还有 在pos右边并且初速度向右+1的蚂蚁不会对A造成任何影响
这两种类型的蚂蚁可以忽略掉
然后就剩下在pos左边速度向右的蚂蚁(设为left类蚂蚁)和在pos右边速度向左的蚂蚁(设为right类蚂蚁)了
还要观察出一个结论是 题目实质上每个蚂蚁的相对位置不会变化
也就是说假设a在b左边 那么不可能在一个时间a跑b的右边去了
可以模拟一下 然后把模型转化
这样可以推出蚂蚁A不会掉落的情况只有是在它左边left类蚂蚁个数==它右边right类蚂蚁个数
其它情况蚂蚁A都会掉下去
你可以想象成蚂蚁A左边离它最近的蚂蚁和右边离它最近的蚂蚁抵消掉 左边第二近的与右边第二近的抵消掉…
至于掉到哪边 就看left和right类蚂蚁哪个少些 哪个少它就掉哪边
掉的时间点: left蚂蚁和right蚂蚁其中一类抵消完后剩下的第一个蚂蚁掉落时间就是A的掉落时间了。

#include<iostream>
#include<set>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
using namespace std;

struct ant{
    int position;
    int speed;
}a[1005];
int main()
{
    int n;
    int l[105],r[105];//左边向右走的蚂蚁坠落所花时间,右边向左走的蚂蚁数量坠落所花时间
    while(cin>>n)
    {
        int flag=0;//可以看成原点处,也是A蚂蚁的位置
        for(int i=0;i<n;i++)
        {
            cin>>a[i].position>>a[i].speed;
            if(a[i].speed==0)
                flag=a[i].position;
        }
        int ln=0,rn=0;//A蚂蚁左边向右走的蚂蚁数量和在右边向左走的蚂蚁数量
        for(int i=0;i<n;i++)
        {
            if(a[i].position<flag&&a[i].speed>0) l[ln++]=100-a[i].position;//记录向右走的蚂蚁,坠落时所花的时间
            else if(a[i].position>flag&&a[i].speed<0) r[rn++]=a[i].position;//记录向左走的蚂蚁,坠落时所花时间
        }
        if(ln==rn)cout<<"Cannot fall!"<<endl;
        else if(ln>rn)
        {
            sort(l,l+ln);
            cout<<l[rn]<<endl;
        }
        else{
            sort(r,r+rn);
            cout<<r[ln]<<endl;
        }
    }
    return 0;
}

相关标签: acm 算法