Piotr`s Ants

这篇博客介绍了如何通过排序和数据结构优化来解决一个复杂问题,即蚂蚁在木棍上移动并碰撞的情景。通过建立蚂蚁的初始状态,忽略蚂蚁的身份,仅关注位置变化,可以将碰撞转化为位置次序的更新。通过两次排序和位置次序的不变性,可以有效地计算出每只蚂蚁在给定时间后的状态,避免了直接模拟可能导致的时间浪费。这种方法适用于处理有大量交互的复杂系统。
摘要由CSDN通过智能技术生成

题目:

一根长度为L厘米的木棍上有n只蚂蚁,每只蚂蚁要么朝左爬,要么朝右爬,速度为1cm/s。当两只蚂蚁相遇时,两者同时调头,如果蚂蚁爬超出木棍两端则掉下。给出每只蚂蚁初始的位置和朝向,计算T秒之后每只蚂蚁的位置。

【Input】

输入的第一行为数据组数。之后每组数据:第一行为三个正整数L,T,n(0<=n<=10000);以下n行每行输入一个正整数x和一个特定字母(L或R),描述一只蚂蚁的初始状态,其中,整数x为蚂蚁距离木棍左端的距离,字母表示初始朝向(L左R右)。

【Output】

对于每组数据,输出n行,按顺序输出每只蚂蚁的位置和朝向(Turning表示正好相遇)。在T秒及之前掉下木棍的蚂蚁输出Fell off。

【Sample Input】

2
10 1 4
1 R
5 R
3 L
10 R
10 2 3
4 R
5 L
8 R

【Sample Output】

2 Turning
6 R
2 Turning
Fell off

3 L
6 R
10 R

 分析:

首先排除模拟,因为碰头的操作次数可能会非常多,而有多组数据,时间上会有极大的浪费。既然模拟不好,那就想办法从模拟过程中找到特点和规律替代模拟。

在纸上画一横轴,描几个点表示蚂蚁。当蚂蚁碰头时,它们的方向会发生变化,而变化后又会碰头其他蚂蚁。想要描述整个T秒内方向的变化显然比较复杂。那如何能规避方向的问题呢?

假设横轴上两黑点碰头,一黑点向前,另一个向后。都是黑点,它们的运动就像路过对方,而没有碰头一样......假设所有蚂蚁都没有名字,那么因此,蚂蚁的运动就可以忽略碰头了。因为从过程上来看,假设一蚂蚁从(1, R),也即位置1,向右,两秒后没碰头变为(3,R)。如果忽略身份的话那么不管中间多少只蚂蚁碰了多少次头,总有一只蚂蚁会达到(3,R),因为忽略身份后所有蚂蚁都是一样的,他们之间就没有所谓的“碰头”了。

现在问题就转到了身份name上。有了身份差别便有了位置次序,先创建两个结构体数组beginendbegin储存原始信息,end储存碰头不变向的运动后信息,对两者排序,保证在木棍上的位置是升序的。因为碰到即转向,蚂蚁的位置次序(身份序号)在运动前后是不变的,这样就把位置次序和输入顺序联系了起来。而后创建intrealName数组储存位置次序与输入顺序的关系。通过运动前左端排序第i的蚂蚁的输入次序name,得到不碰头运算后原次序第i个蚂蚁的当前排序,再.place即可得到原次序第i蚂蚁的位置,这样便可有序输出。

例:

输入:

1
10 3 4
8 R
2 L
10 L
4 R

                    ←       →            →       ←                    方向        

                    1       3            0       2                    编号

              ------·-------·------------·-------·                    木棍 length = 10

                    2       4            8       10                   位置

结构体数组begin之中存储的身份: {0, 1, 2, 3};位置:{2, 0, 3, 1};

对数组begin根据place排序:身份: {1, 3, 0, 2};位置:{0, 1, 2, 3};



T秒后:

                                    → ←                                方向        

             1 (fell)               3,0            2 (fell)            编号

             . ----------------------·----------- ·                    木棍 length = 10

            -1                       7            11                   位置

end数组:身份:{1, 3, 0, 2};对应T秒前begin,无变化。

代码:

package Test;
import org.jetbrains.annotations.NotNull;

import java.util.*;
/**
 * @deprecated Piotr`s Ants, UVa 10881
 * @author WitMoy
 * @version V1.8
 * @date : 2022-07-28 16:31
 */
public class PiotrAnts {
    private static Scanner in = new Scanner(System.in);
    public static void main(String[] args) {
        int times = in.nextInt();
        while(times-- > 0){
            int L, T, n;
            L = in.nextInt();
            T = in.nextInt();
            n = in.nextInt();
            //创建Ants数组
            Ants[] begin = new Ants[n], end = new Ants[n];
            for(int i = 0; i < n; i++){
                begin[i] = new Ants();
                end[i] = new Ants();
            }
            //输入数据
            for(int i = 0; i < n; i++){
                int place = in.nextInt();
                String direction = in.next();
                boolean t = Objects.equals(direction, "L");
                int positive = 1;
                if(t){
                    positive *= -1;
                }
                begin[i].setPlace(place);
                begin[i].setDirection(direction);
                begin[i].setName(i);
                //计算“路过”的最终位置
                end[i].setPlace(place + T * positive);
                end[i].setDirection(direction);
            }
            //为Java自带的sort写排序规则,也可以自己写排序函数
            Comparator<Ants> myComparator = new MyComparator();
            //对运动前后的蚂蚁按照位置从左向右排序
            Arrays.sort(begin, myComparator);
            Arrays.sort(end, myComparator);
            //通过位置次序的不变性联系原输出次序
            int[] realPlace = new int[n];
            for(int i = 0; i < n; i++){
                /*因为位置次序不变,原来排第一的在之后还在第一,而它
                的输出次序为begin[].getName();*/
                //次序为begin[i].getName()的排第i
                realPlace[begin[i].getName()] = i;
            }
            //先不管身份,更新每个节点的输出状态
            for(int i = 0; i < n; i++){
                if(end[i].getPlace() < 0 || end[i].getPlace() > L){
                    end[i].setDirection("Fell off");
                }
                else if(i < n - 1 && end[i].getPlace() == end[i + 1].getPlace()){
                    end[i].setDirection("Turning");
                    end[i + 1].setDirection("Turning");
                }
            }
            //输出
            for(int i = 0; i < n; i++){
                //原输出次序为i的排序为originalOrder
                int NowOrder = realPlace[i];
                int place = end[NowOrder].getPlace();
                if(place >= 0 && place <= L){
                    System.out.print(place + " ");
                }
                System.out.println(end[NowOrder].getDirection());
            }
            System.out.println();
        }
    }
}
class Ants{
    private int place;
    private String direction;
    private int name;
    public int getName() {
        return name;
    }
    public void setName(int name) {
        this.name = name;
    }
    public String getDirection() {
        return direction;
    }
    public void setDirection(String direction) {
        this.direction = direction;
    }
    public int getPlace() {
        return place;
    }
    public void setPlace(int place) {
        this.place = place;
    }
}
class MyComparator implements Comparator<Ants>{
    public int compare(@NotNull Ants a, @NotNull Ants b) {
        return a.getPlace() - b.getPlace();
    }
}
/*测试数据
2
10 1 4
1 R
5 R
3 L
10 R
10 2 3
4 R
5 L
8 R

2 Turning
6 R
2 Turning
Fell off

3 L
6 R
10 R
* */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿白|

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值