编程思想训练--poj 1852--ants(题解与拓展)

引入:

在竞赛中我们经常会遇到很多很多你望而却步的题,,不是你的基础知识不扎实,而是这些题都考的是你的思维能力,而不是你学了多少的算法,,,,

下面我们有一道题开始:--//图片来自:

https://blog.csdn.net/chichoxian/article/details/20871497

这道题十分经典,相信oier们都做过.

我就在此给同我一样菜的oier讲讲这道题的思路吧。

然后我发现了一个事实,好像我贴的是英文题目!!!

重来一遍:

描述:

一群蚂蚁在长度为1厘米的水平杆上行走,每根杆的速度恒定为1厘米/秒。当一只行走的蚂蚁到达极的一端时,它会立即脱落。当两只蚂蚁相遇时,它们会转身向相反方向开始行走。我们知道蚂蚁在杆子上的原始位置,不幸的是,我们不知道蚂蚁行走的方向。你的任务是计算所有蚂蚁从杆上掉下来所需的最早和最晚时间。

输入:

第一行输入包含一个整数,给出后面的案例数。每种情况的数据都以两个整数开始:极点的长度(cm)和n,极点上的蚂蚁数量。这两个数字后面跟着n个整数,它们将每个蚂蚁在杆上的位置作为从杆的左端测量的距离,没有特别的顺序。所有输入整数都不大于1000000,它们由空格分隔。

输出:

对于每种输入情况,输出由单个空格分隔的两个数字。第一个数字是所有蚂蚁从杆上掉下来的最早时间(如果他们的步行路线选择得恰当),第二个数字是最新可能的时间。 

题意概括:一些蚂蚁以1cm/s的速度在长度为Lcm的水平杆上爬行,爬到端点就会掉下去。当两只蚂蚁相遇,它们就会立刻掉头返回。已知L和一开始每只蚂蚁的位置,但不知道它们的方向,求它们最早何时全部掉落,最迟何时全部掉落。

最多1,000,000只蚂蚁

分析:

这里大家首先想到的是模拟吧(哈哈,和我一样),但是模拟用循环嵌套的话对于每只蚂蚁,都有两种情况,那么一共就是2^n的时间复杂度,看一下题设要求,(好吧这里忘写了n最大是10^6),如果用循环模拟这种n^3的方法的话,必定RE,所以我们要想出一种在一秒内就可以解决的方法,1秒的时间复杂度对于此题来讲是只能为O(n)或则O(1)的,所以我们要找出这个O(n)的方法(这个已经够了),,,,,

怎么做呢?我反正是不会的,老师讲后我才明白,,,

我们先看题:这里是说每只蚂蚁互相碰到后就会掉头,然后每只蚂蚁间持续这样,直到它们都掉下去,我们想一想,两只蚂蚁碰头后又返回,不就像互相继承了对方的方向吗???

看图:更好理解

我们可以这样形象的理解:蚂蚁一和蚂蚁二身体是中空的,它们互相穿过了对方,只不过交换了对方的名号而已,,,

其实,说了这么多,我只是想让你想明白一点:

蚂蚁都是一样的,所以当两只蚂蚁相遇时转向跟没转向也是一样的,因为每只蚂蚁都会继承另一只蚂蚁的方向,所以可以视为蚂蚁方向没有变过,所以最短时间就是所有蚂蚁距端点最近距离的最大值,最长时间就是所有蚂蚁距端点最远距离的最大值。

好了贴代码吧:(此处只放核心代码)

这道题你懂了吗???

那我们现在再来拓展一下;

这道题看名字肯定与第一题相关,但是做起来可不一样,我们发现最难的就是定下蚂蚁的状态,并且输出它的位置。

解题思路:

定义一个数据结构 “”蚂蚁” ---用struct可以记载当前位置,编号(第几只蚂蚁),和左右方向

并且我们规定左为-1,右为1,转向为0

还要用三个数组分别记载:1.原蚂蚁序列 2.后来的蚂蚁序列 3.各个蚂蚁的编号--(其实就是它的位置,原因在下面!!)

我们在图上模拟可得,蚂蚁之间互相碰到便掉头,则他们之间巧妙的维护了一种相对平衡的位置顺序,,你模拟看看就知道了,,,

然后我们其实并不能一个一个的算蚂蚁相撞的位置在哪,然后判定他们的方向,因为,时间复杂度太大,工作量也太大,我们直接现将原蚂蚁序列的顺序记录下来,并将其排序,,先不管他们会不会掉下去,直接把他们的位置累加出来,,最后来判断(其中的一个操作便是排序),最后排序输出就行了!

贴代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 10005
using namespace std;

struct Ant
{
    int id;    //
    int p;
    int d;
    bool operator < (const Ant &other) const
    {
        return p < other.p;
    }
}before[maxn],after[maxn];

const char dirName[][10] = {"L","Turning","R"};

int order[maxn];

int main()
{
    int L,T,n;
    scanf("%d%d%d",&L,&T,&n);//输出 
    
    for(int i = 0;i < n;i++)
    {
        int p,d;
        char c;
        scanf("%d %c",&p,&c);
        d = (c == 'L' ? -1 : 1);
        before[i] = (Ant){i,p,d};//初始化 
        after[i] = (Ant){0,p+T*d,d};
    }
    
    
    sort(before,before+n);//排序 
    for(int i = 0;i < n;i++)
        order[before[i].id] = i;//记录下相对位置---编号 
        
    
    sort(after,after+n);//排序 
    for(int i = 0;i < n - 1;i++)
        if(after[i].p == after[i+1].p)
            after[i].d = after[i+1].d = 0;//判断是否相撞--即处于一点 
    
    for(int i = 0;i < n;i++)
    {
        int a = order[i];
        if(after[a].p < 0 || after[a].p > L)//输出 
            printf("Fell off\n");
        else printf("%d %s\n",after[a].p,dirName[after[a].d+1]);
    }
    return 0;
}

好了,,就到这吧!!!qwq

 

转载于:https://www.cnblogs.com/liuyuhao040610/p/11252536.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值