题目:
一根长L厘米的木棍上有n只蚂蚁,每只蚂蚁要么朝左爬,要么朝右爬,速度为1cm/s。当两只蚂蚁相撞时,二者同时掉头。给出每只蚂蚁的初始位置和朝向,计算T秒之后每只蚂蚁的位置。
对于每组数据,按输入顺序输出每只蚂蚁的位置和朝向,Turning表示正在碰撞。在第T秒之前已经掉下木棍的蚂蚁(正好爬到木棍边缘的不算)输出Fell off。
样例输入
2
10 1 4
1 R
5 R
3 L
10 R
10 2 3
4 R
5 L
8 R
样例输出:
Case #1:
2 Turning
6 R
2 Turning
Fell off
Case #2:
3 L
6 R
10 R
思路:
当蚂蚁因碰撞而掉头时,看上去像两个点”对穿而过“ ,那么如果把蚂蚁看成小点,就只需要独立计算出每只蚂蚁T秒之后的位置即可。
但是,整体看来是”对穿而过“,对于每一只蚂蚁可不是这样。蚂蚁1的初始状态为(1,R),因此一定有一只蚂蚁在2s之后处于(3,R)的状态,但是这只蚂蚁不一定是蚂蚁1。因此我们需要搞清楚目标状态中”谁是谁“。
因此,所有蚂蚁的相对顺序是不变的。
将所有蚂蚁目标位置从小到大排序,则从左到右的每个位置对应于初始状态下从左到右的每只蚂蚁。因为题目中不一定按蚂蚁从左到右的顺序输入,因此需要预处理出输入中的第i只蚂蚁的序号order[i].
步骤:
先保存两个结构体数组:一个是初始位置的before数组,另一个是T秒之后的目标位置的after数组。
先对before数组按位置从左到右排序,处理出输入中的第i只蚂蚁的序号order[i].
再对after数组按位置从左到右排序,之后从头开始遍历,每次和下一个比较,如果位置相同表明正在Turning。
因为要按输入顺序输出每只蚂蚁的位置和朝向,所以每次取序号a=order[i],先判断这只蚂蚁是否已经掉下去,如果没有,再输出它的位置after[a].p和状态dirName[after[a].d+1]。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
struct node{
int id;//输入顺序
int p;//位置
int d;//朝向 -1:左 0:turning 1:右
//按位置从左到右排序
bool operator<(const node& a)const{
return p<a.p;}
}before[N],after[N];
//储存状态名称
char dirName[][15]={"L","Turning","R"};
int order[N];//输入的第i只蚂蚁是终态中的左数第order[i]只蚂蚁
int main(){
int k;
scanf("%d",&k);
for(int kase=1;kase<=k;kase++){
int L,T,n;
printf("Case #%d:\n",kase);
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]=(node){i,p,d};
//目标数组
after[i]=(node){0,p+T*d,d};//这里的id是未知的
}
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;//修改为Turning状态
}
}
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]);
}
puts("");
}
return 0;
}
类似穿墙而过的思路的还有2014年蓝桥杯的这道题:
第七题:蚂蚁感冒
题目描述
长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。
当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。
这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。
【数据格式】第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。
接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。
要求输出1个整数,表示最后感冒蚂蚁的数目。
例如,输入:
3
5 -2 8
程序应输出:
1再例如,输入:
5
-10 8 -20 12 25
程序应输出:
3
分析:
对对碰==穿过对方。
如果最初感冒的蚂蚁向右,那么它右边的所有反向的蚂蚁都将被感染。
如果右边存在和它碰头的蚂蚁,它就会反向,那么此时它后面的
原来和它同向的蚂蚁都将被感染。
向左思路类似。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int a[N],n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
int x=a[0];
if(x>0){//如果感冒的蚂蚁向右
int ans=1;//最初感冒的蚂蚁只有1只
for(int i=0;i<n;i++){
if(a[i]<0&&-a[i]>x){//找反向的在它前面的蚂蚁
ans++;
}
}
if(ans!=1){//如果对碰了,会反向
for(int i=0;i<n;i++){
if(a[i]>0&&a[i]<x){//找同向的在它后面的蚂蚁
ans++;
}
}
}
printf("%d",ans);
}
else if(x<0){//如果感冒的蚂蚁向左
int ans=1;//最初感冒的蚂蚁只有1只
for(int i=0;i<n;i++){
if(a[i]>0&&a[i]<-x){//找在它后面的反向的蚂蚁
ans++;
}
}
if(ans!=1){//如果对碰了
for(int i=0;i<n;i++){
if(a[i]<0&&-a[i]>-x){//找在它前面的同向的蚂蚁
ans++;
}
}
}
printf("%d",ans);
}
return 0;
}