微软笔试题-highways

题目大意

    一条单向的高速公路上有N辆车,在0时刻,每辆车分别在起点A[0],A[1]....处开始从北向南出发,每辆车有个终点B[0],B[1]....且每辆车有个限制速度 V[0],V[1]... 路上不能超车,即车X可能因为速度较慢,会挡住后面的车Y,但车X到达它的终点之后就消失了,不会再阻挡后面的车。 
    求出每辆车到达各自终点的最短时间。题目见 http://hihocoder.com/contest/hiho65/problem/1

题目分析

  此题采用模拟的方法来解决,对于每辆车都有一个终点,题目也需要我们求出每辆车到达它的终点的最短时间。但是在车行进的过程中,可能会由于其他车的阻挡而无法始终以最大速度行进,这需要我们知道每辆车在什么时间段可以最大速度行进,什么时间段只能紧贴着前车行进。这样来解决就比较麻烦,可以转换一下: 
(1)由于车道为单向,因此后车不会对前面的车产生影响,而后车受到前面车的影响。因此将车按照起始位置进行排序,然后按照从前向后的顺序确定每辆车到达其终点的顺序。 
(2)因为考虑车A时,车A前面的车会对车A造成影响,而且影响会在前面的某车B到达车B的终点之后结束。因此,所有车的终点,都应该被视为一个重要因素,所以,保存车A前面的所有车到达车A前面的那些所有终点的时间。 
(3)再考虑一下,发现不需要保存A前面所有车B1,B2...到达A前面所有终点Fi, Fi+1....的时间。我们先对A前面的终点排序,从A出发到达那些终点肯定是按照从近到远的顺序到达。我们按照模拟的方法,从近到远分阶段考察A到达Fi,Fi+1,Fi+2..的时间,考虑A从Fj到Fj+1的时间,因为考虑A之前,已经获得了A前面的那些车到达Fj+1的时间,此时,我们需要知道的是A前面的那些车到达Fj的最大的时间M,若A以全速从Fj到达Fj+1的时间(记录的是从A的起点到达Fj+1的时间)大于M,则说明A的速度不够,否则说明A的前面的车阻挡A。两种情况下,更新M,以及A到达Fj+1的时间。当A到达其终点A.end的时候,就记录A到达的时间,该时间就是A到达的最短时间。

所以,只需要记录每个终点有车到达的最大的时间即可。

实现(c++)

#define _CRT_SECURE_NO_WARNINGS	
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define MAX_CAR_NUM 1005

/*
此题采用模拟的方法来解决,对于每辆车都有一个终点,题目也需要我们求出每辆车到达它
的终点的最短时间。但是在车行进的过程中,可能会由于其他车的阻挡而无法始终以最大速度行进,
这需要我们知道每辆车在什么时间段可以最大速度行进,什么时间段只能紧贴着前车行进。这样来解决就
比较麻烦,可以转换一下:
(1)由于车道为单向,因此后车不会对前面的车产生影响,而后车受到前面车的影响。因此将车按照起始位置进行
排序,然后按照从前向后的顺序确定每辆车到达其终点的顺序。
(2)因为考虑车A时,车A前面的车会对车A造成影响,而且影响会在前面的某车B到达车B的终点之后结束。因此,
所有车的终点,都应该被视为一个重要因素,所以,保存车A前面的所有车到达车A前面的那些所有终点的时间。
(3)再考虑一下,发现不需要保存A前面所有车B1,B2...到达A前面所有终点Fi, Fi+1....的时间。我们先对A前面的终点排序,
从A出发到达那些终点肯定是按照从近到远的顺序到达。我们按照模拟的方法,从近到远分阶段考察A到达Fi, Fi+1,Fi+2..的时间,
考虑A从Fj到Fj+1的时间,因为考虑A之前,已经获得了A前面的那些车到达Fj+1的时间,此时,我们需要知道的是A前面的那些车
到达Fj的最大的时间M,若A以全速从Fj到达Fj+1的时间(记录的是从A的起点到达Fj+1的时间)大于M,则说明A的速度不够,否则说明
A的前面的车阻挡A。两种情况下,更新 M,以及A到达Fj+1的时间。当A到达其终点A.end的时候,就记录A到达的时间,该时间
就是A到达的最短时间。

所以,只需要记录每个终点有车到达的最大的时间即可。
*/
int gEndPoint[MAX_CAR_NUM];	//保存各个终点的位置
double gMaxEndTime[MAX_CAR_NUM];
int gCarIndex[MAX_CAR_NUM];
struct Car{
	int start_point;
	int end_point;
	int end_index;
	double v_limit;
	double arrive_time;
};
Car gCars[MAX_CAR_NUM];

//用于对车的索引按照车的起点位置排序,
bool Compare1(int index1, int index2){
	return gCars[index1].start_point < gCars[index2].start_point;
}

//根据车A的起点,找到车A需要经过的那些终点的起始序号(终点经过排序)
int GetEndIndex(int start, int n){
	int beg = 0, end = n;
	while (beg < end){
		int mid = (beg + end) / 2;
		if (gEndPoint[mid] == start)
			return mid;
		else if (gEndPoint[mid] > start)
			end = mid;
		else
			beg = mid + 1;
	}
	return beg;
}

int main(){
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++){
		scanf("%d %d %lf", &gCars[i].start_point, &gCars[i].end_point, &gCars[i].v_limit);
		gEndPoint[i] = gCars[i].end_point;
		gCarIndex[i] = i;
	}
	memset(gMaxEndTime, 0, sizeof(gMaxEndTime));

	//对车的索引排序 gCarIndex[0]表示起点最小的车在gCar中的位置,gCarIndex[1]表示起点第二小的车在gCar中的位置
	sort(gCarIndex, gCarIndex + n, Compare1); 
	

	//对终点进行排序
	sort(gEndPoint, gEndPoint + n);
	
	for (int i = 0; i < n; i++){
		gCars[i].end_index = GetEndIndex(gCars[i].end_point, n);	//二分法确定车的终点序号,用于当车到达终点时记录
	}

	//从前向后考虑
	for (int i = n - 1; i >= 0; i--){
		int start_index = GetEndIndex(gCars[gCarIndex[i]].start_point, n);
		double time = 0, point = gCars[gCarIndex[i]].start_point, v_limit = gCars[gCarIndex[i]].v_limit;
		//考察车A的路程经过的那些终点即可
		for (int index = start_index; index <= gCars[gCarIndex[i]].end_index; index++){
			if (time + (gEndPoint[index] - point) / v_limit > gMaxEndTime[index]){ //判断是否前车阻挡
				time += (gEndPoint[index] - point) / v_limit;								
				gMaxEndTime[index] = time;
			}
			else{
				time = gMaxEndTime[index];
			}
			if (gCars[gCarIndex[i]].end_index == index){//到达终点进行更新
				gCars[gCarIndex[i]].arrive_time = gMaxEndTime[index];
			}
			
			point = gEndPoint[index];
		}
	}
	for (int i = 0; i < n; i++){
		printf("%.2lf\n", gCars[i].arrive_time);
	}
	return 0;
}

 

posted @ 2015-09-29 14:24  农民伯伯-Coding  阅读(357)  评论(0编辑  收藏  举报