PAT 1095 Cars on Campus


这一题的大意是说给出N条记录,这N条记录里面有进入停车场的也有出停车场的,现在我们要做的是给出K条询问,问在某一个时刻,有多少辆车在这个停车场中,在一天内停在停车场中时间最长的车牌号是多少,如果有多个,按照车牌号从小到大输出。

在这一题中给出的记录是不按照时间顺序的,而题目要求必须保证一条进入的记录和一条出去的记录相匹配,如果不存在相匹配的记录,那么这个记录就该被忽略,

因此如何确定哪些记录是相匹配的是第一要务。

我们可以通过进行自定义排序,先按照车牌大小排序,再按照时间大小排序这样我们就把同一辆车的相近时间挨在一起。

这样我们只需要保证相邻两个记录的状态的是不同的,一个是in,一个out即可匹配。

cpp 复制代码
for(int i=0;i<N;i++)
	{
		char s[20];
		int hh;
		int mm;
		int ss;
		char status[10];
		scanf("%s",s);
		scanf("%d:%d:%d",&hh,&mm,&ss);
		scanf("%s",status);
		n[i].plate_number=s;
		n[i].times=getsecond(hh,mm,ss);
		n[i].status=status;
	} 
	sort(n,n+N,cmp);
	//将配对的时间筛选出来
	int cnt=0;
	int maxxtime=0;
	for(int i=0;i<N-1;i++)
	{
	   if(n[i].plate_number==n[i+1].plate_number)	
	   {
	   	     if(n[i].status!=n[i+1].status&&n[i].status=="in"&&n[i+1].status=="out")
	   	     {
	   	        //说明它们配对	
	   	        r[cnt].plate_number=n[i].plate_number;
	   	        r[cnt].starttime=n[i].times;
	   	        r[cnt].endtime=n[i+1].times;
	   	        long_car[n[i].plate_number]+=n[i+1].times-n[i].times;
	   	        maxxtime=max(long_car[n[i].plate_number],maxxtime);
			     cnt++;
			 } 
	   }
	} 

在匹配的过程中,我们也可以同时统计在停车场中所待的最多的时间,并用map将时间段和车牌号一一对应。

然后我们就按照题目要求进行询问,只需要判断一个时间点是不是在我们筛选过后的某一个记录的开始时间和结束时间之内即可,注意这个时间点>=开始时间,小于结束时间,统计有多少条这样的记录,即为在这个时间点有多少个车辆(因为同一个车辆不可能在一个时刻出现多次),

最后遍历map输出与最长时间相等的key值,最后再输出最长时间

因为map是自动按照key升序排列的,因此可以直接满足车牌号从小到大输出的题目要求。

cpp 复制代码
#include<bits/stdc++.h>
#include<iostream> 
using namespace std;
//每一个门口我们可以收集进出时间和汽车通过大门的数量
//你应该辨别在某一个特定的时间点,汽车停在校园里面的数量
//在一天的结束找到已经停泊最长时间段的汽车 
int N;
int K;
//每一个记录包括 车牌号 时间 状态 
//每一个进入的记录和下一条是同一个汽车的出记录相匹配
//任何无法匹配的记录都被忽略 
//确保至少有一个车跟输入相匹配
//没有车能同时进入和出去
//询问被给出的以一个递增的顺序 
struct node
{
	string plate_number;
	int times;
	string status;
}n[10005];
int getsecond(int h,int m,int s)
{
	return h*3600+m*60+s;
}
bool cmp(node a,node b)
{
	if(a.plate_number<b.plate_number)
	{
		return true;
	}
	else if(a.plate_number==b.plate_number)
	{
		if(a.times<b.times)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return false;
	}
}
struct record
{
	string plate_number;
	int starttime;
	int endtime;
}r[10005];
map<string,int> long_car;

bool cmp2(record a,record b)
{
    if(a.starttime<b.starttime)
	{
		return true;
	}
	else
	{
	    return false;	
	}	
} 
int main()
{
	cin>>N;
	//N条记录
	//K次询问 
	cin>>K;
	for(int i=0;i<N;i++)
	{
		char s[20];
		int hh;
		int mm;
		int ss;
		char status[10];
		scanf("%s",s);
		scanf("%d:%d:%d",&hh,&mm,&ss);
		scanf("%s",status);
		n[i].plate_number=s;
		n[i].times=getsecond(hh,mm,ss);
		n[i].status=status;
	} 
	sort(n,n+N,cmp);
	//将配对的时间筛选出来
	int cnt=0;
	int maxxtime=0;
	for(int i=0;i<N-1;i++)
	{
	   if(n[i].plate_number==n[i+1].plate_number)	
	   {
	   	     if(n[i].status!=n[i+1].status&&n[i].status=="in"&&n[i+1].status=="out")
	   	     {
	   	        //说明它们配对	
	   	        r[cnt].plate_number=n[i].plate_number;
	   	        r[cnt].starttime=n[i].times;
	   	        r[cnt].endtime=n[i+1].times;
	   	        long_car[n[i].plate_number]+=n[i+1].times-n[i].times;
	   	        maxxtime=max(long_car[n[i].plate_number],maxxtime);
			     cnt++;
			 } 
	   }
	} 
	sort(r,r+cnt,cmp2);
	for(int i=0;i<K;i++)
	{
		int hh;
		int mm;
		int ss;
		scanf("%d:%d:%d",&hh,&mm,&ss);
		int cut_time=getsecond(hh,mm,ss);
		unordered_map<string,int> mp; 
		int number=0;
		for(int j=0;j<cnt;j++)
		{
			if(cut_time>=r[j].starttime&&cut_time<r[j].endtime)
			{
				//说明确实发现在这个时间有一条记录在 
			    mp[r[j].plate_number]++;
				number++;	
			}
			else if(cut_time<r[j].starttime)
			{
				break;
			}
		}
		cout<<number<<endl;
	}
	//
	int index=0;
	for(auto it: long_car)
	{
	    if(it.second==maxxtime)
	    {
	    	cout<<it.first;
	    	cout<<" ";
		}
	 } 

	 printf("%02d:%02d:%02d",maxxtime/3600,maxxtime%3600/60,maxxtime%60);
	
    return 0;
}

但是这样写会有两个测试点超时,原因是:

cpp 复制代码
for(int i=0;i<K;i++)
	{
		int hh;
		int mm;
		int ss;
		scanf("%d:%d:%d",&hh,&mm,&ss);
		int cut_time=getsecond(hh,mm,ss);
		unordered_map<string,int> mp; 
		int number=0;
		for(int j=0;j<cnt;j++)
		{
			if(cut_time>=r[j].starttime&&cut_time<r[j].endtime)
			{
				//说明确实发现在这个时间有一条记录在 
			    mp[r[j].plate_number]++;
				number++;	
			}
			else if(cut_time<r[j].starttime)
			{
				break;
			}
		}
		cout<<number<<endl;
	}

双重循环时间复杂度过高

那么如何优化呢?

我们可以采用差分+前缀和的方式,将每一个记录说在的时间段通过差分标记数组的方式标记到一天内的时间中,再用前缀和算出每一个时间点有多少个车辆

如下:

cpp 复制代码
sort(r,r+cnt,cmp2);
	//23*3600*59*60+59=
    for(int i=0;i<cnt;i++)
    {
    	a[r[i].starttime]+=1;
    	a[r[i].endtime]-=1;
	}
	for(int i=0;i<=23*3600+59*60+59;i++)
	{
		if(i==0)
		{
			sum[i]=a[i];
		}
		else
		{
			sum[i]=sum[i-1]+a[i];
		}
	}
	for(int i=0;i<K;i++)
	{
		int hh;
		int mm;
		int ss;
		scanf("%d:%d:%d",&hh,&mm,&ss);
		int cut_time=getsecond(hh,mm,ss);
		cout<<sum[cut_time]<<endl;
    }

完整代码如下:

cpp 复制代码
#include<bits/stdc++.h>
#include<iostream> 
using namespace std;
//每一个门口我们可以收集进出时间和汽车通过大门的数量
//你应该辨别在某一个特定的时间点,汽车停在校园里面的数量
//在一天的结束找到已经停泊最长时间段的汽车 
int N;
int K;
//每一个记录包括 车牌号 时间 状态 
//每一个进入的记录和下一条是同一个汽车的出记录相匹配
//任何无法匹配的记录都被忽略 
//确保至少有一个车跟输入相匹配
//没有车能同时进入和出去
//询问被给出的以一个递增的顺序 
struct node
{
  string plate_number;
  int times;
  string status;
}n[10005];
int getsecond(int h,int m,int s)
{
  return h*3600+m*60+s;
}
bool cmp(node a,node b)
{
  if(a.plate_number<b.plate_number)
  {
  	return true;
  }
  else if(a.plate_number==b.plate_number)
  {
  	if(a.times<b.times)
  	{
  		return true;
  	}
  	else
  	{
  		return false;
  	}
  }
  else
  {
  	return false;
  }
}
struct record
{
  string plate_number;
  int starttime;
  int endtime;
}r[10005];
map<string,int> long_car;
bool cmp2(record a,record b)
{
  if(a.starttime<b.starttime)
  {
  	return true;
  }
  else
  {
      return false;	
  }	
} 
int sum[90000];
  int a[90000];
int main()
{
  cin>>N;
  //N条记录
  //K次询问 
  cin>>K;
  for(int i=0;i<N;i++)
  {
  	char s[20];
  	int hh;
  	int mm;
  	int ss;
  	char status[10];
  	scanf("%s",s);
  	scanf("%d:%d:%d",&hh,&mm,&ss);
  	scanf("%s",status);
  	n[i].plate_number=s;
  	n[i].times=getsecond(hh,mm,ss);
  	n[i].status=status;
  } 
  sort(n,n+N,cmp);
  //将配对的时间筛选出来
  int cnt=0;
  int maxxtime=0;
  for(int i=0;i<N-1;i++)
  {
     if(n[i].plate_number==n[i+1].plate_number)	
     {
     	     if(n[i].status!=n[i+1].status&&n[i].status=="in"&&n[i+1].status=="out")
     	     {
     	        //说明它们配对	
     	        r[cnt].plate_number=n[i].plate_number;
     	        r[cnt].starttime=n[i].times;
     	        r[cnt].endtime=n[i+1].times;
     	        long_car[n[i].plate_number]+=n[i+1].times-n[i].times;
     	        maxxtime=max(long_car[n[i].plate_number],maxxtime);
  		     cnt++;
  		 } 
     }
  } 
  sort(r,r+cnt,cmp2);
  //23*3600*59*60+59=
  for(int i=0;i<cnt;i++)
  {
  	a[r[i].starttime]+=1;
  	a[r[i].endtime]-=1;
  }
  for(int i=0;i<=23*3600+59*60+59;i++)
  {
  	if(i==0)
  	{
  		sum[i]=a[i];
  	}
  	else
  	{
  		sum[i]=sum[i-1]+a[i];
  	}
  }
  for(int i=0;i<K;i++)
  {
  	int hh;
  	int mm;
  	int ss;
  	scanf("%d:%d:%d",&hh,&mm,&ss);
  	int cut_time=getsecond(hh,mm,ss);
  	cout<<sum[cut_time]<<endl;
  }
  for(auto it: long_car)
  {
      if(it.second==maxxtime)
      {
      	cout<<it.first;
      	cout<<" ";
  	}
   } 

   printf("%02d:%02d:%02d",maxxtime/3600,maxxtime%3600/60,maxxtime%60);
  
  return 0;
}

完美通过

总结:这一题首先是要通过自定义排序来实现对符合条件的记录的筛选,然后一个难点是优化查询,用差分+前缀和的方式,一个难点是用map的方式存储车牌号和总时间的映射关系方便按照题意输出。需要我们对上述知识点熟练掌握。

相关推荐
MicroTech20253 小时前
激光点云快速配准算法创新突破,MLGO微算法科技发布革命性点云配准算法技术
人工智能·科技·算法
Cathy Bryant3 小时前
傅里叶变换(一):简介
笔记·算法·数学建模·信息与通信·傅里叶分析
allan bull4 小时前
在节日中寻找平衡:圣诞的欢乐与传统节日的温情
人工智能·学习·算法·职场和发展·生活·求职招聘·节日
似水এ᭄往昔4 小时前
【C++】--封装红⿊树实现mymap和myset
开发语言·数据结构·c++·算法·stl
咕噜企业分发小米4 小时前
腾讯云向量数据库HNSW索引如何更新?
人工智能·算法·腾讯云
lcreek4 小时前
LeetCode215. 数组中的第K个最大元素、LeetCode912. 排序数组
python·算法·leetcode
Einsail4 小时前
天梯赛题解(3-6)
算法
杜子不疼.4 小时前
【LeetCode 852 & 162_二分查找】山脉数组的峰顶索引 & 寻找峰值元素
算法·leetcode·职场和发展
山楂树の4 小时前
搜索插入位置(二分查找)
数据结构·算法