学校要举办校庆晚宴,要求学生登记就餐时间,以确定在哪个时间段内就餐的学生数最多,从而调食品的供应量。就餐时间被分为了 N 个时间段,其中 N 可能会非常大,可以假 设 N 为 1 亿,也就是 100000000(这要求程序不能声明长度为 N 的数组或定义 N 个变量), 若未考虑此情况则不能得分。
要求:排序算法的平均时间复杂度不得大于 O(MlogM)。
先输入两个数字 N,M,表示就餐时间被分为了 N 个时间段,一共有 M 个学生。 之后程序输入 M 行,每行两个数字,表示第 i 个学生期望的就餐起始时间段和结束时间段。 程序输出若干行,每行两个数字,表示就餐人数最多时间段的起始时间和终止时间。
测试样例:
输入
5 5
1 2
2 3
1 5
3 5
3 4
输出
3 3
样例解释:
就餐人数最多的时间段的起始时间是第三个时间段,终止时间是第三个时间段。 因为在第三个时间段到第三个时间段,2、3、4、5 号学生都在就餐,这个时间段是就餐 人数最多的时间段。
在这里,我把题目分成三个部分
没什么好说的,直接上代码
//录入学生登记的时间段,并判断输入是否合法
void registration(int start[], int finish[], int M, int N)
{
int i;
printf("Please enter the expected time period for each student in turn: \n");
for (i = 0; i < M; i++)//for循环进行输入
{
printf("The expected period of time for %d students: \n", i + 1);
scanf("%d", &start[i]);
scanf("%d", &finish[i]);
if(start[i]<0||finish[i]>N)//排除非法输入
{
assert(0);
}
}
}
这个我会在我的其他博客中详解,这里直接上代码
void merge(int a[],int b[],int min,int max)//归并排序
{
int mid,p;//mid为数组的中间位置,p为从中间位置开始的指针
int min2;//记录了数组最小的位置
int i,j;
min2=min;//min为从最小位置开始的指针
if((max-min)<=1)//如果数组为1则return
{
return;
}
mid=(max+min)/2;//计算中间位置
p=mid;
merge(a,b,min,mid);//向中间位置的左边递归
merge(a,b,mid,max);//向中间位置的右边递归
for(i=min;i<max;)//开始归并排序
{
if(a[min]>a[p])//左边大于右边
{
b[i]=a[p];
p++;
if(p==max)//判断是否排序完成
{
i++;
for(j=min;j<mid;j++,i++)
{
b[i]=a[j];
}
}
else
{
i++;
}
}
if(a[min]<=a[p])//右边大于左边
{
b[i]=a[min];
min++;
if(min==mid)//判断是否排序完成
{
i++;
for(j=p;j<max;j++,i++)
{
b[i]=a[j];
}
}
else
{
i++;
}
}
}
for(i=min2;i<max;i++)//整理排序后的数组
{
a[i]=b[i];
}
}
重要参数
for(i=0;i<M;i++)//复制start和finish数组
{
a1[i]=start[i];
a2[i]=finish[i];
}
merge(a1,start,min,M);//排序start数组
merge(a2,finish,min,M);//排序finish数组
for(j=0,i=M;j<M;i++,j++)//将start数组和finish数组合并
{
a1[i]=finish[j];
}
merge(a1,a2,min,2*M);//排序得到后的数组
i=0;
j=0;
c[j]=a2[i];//第一位赋值
i++;
while(i<2*M)//得到start和finish中出现的所有时间点,且不重复出现,并且有序
{
if(c[j]!=a2[i])
{
j++;
c[j]=a2[i];
}
i++;
}
len=j+1;
c[len]=c[len-1]+1;//多腾一个空间为后续计算时间段人数准备
len++;
for(i=0,j=0,k=0;i<2*M;i++)//计算哪个时间点有人进出
{
while(c[i]==start[j]&&j<M)
{
num[i]++;
j++;
}
while(c[i]==finish[k]&&k<M)
{
num[i+1]--;
k++;
}
}
for(i=1;i<len;i++)//计算每个时间点的人数
{
data[i]=num[i]+data[i-1];
}
for(i=0;i<len;i++)//寻找时间点人数最多的是多少人
{
if(maxnum<=data[i])
{
maxnum=data[i];
}
}
for(i=0,j=0;i<len;i++)//记录时间点人数最多的时间点
{
if(data[i]==maxnum)
{
maxtime[j][0]=c[i];
maxtime[j][1]=c[i];
j++;
}
}
while(j+1<len2&&i<M)//判断是否在一个时间点到另一个时间点都是最大人数
{
if(len2==1)
{
break;
}
if(maxtime[j][1]<finish[i])//该时间点没人选择为就餐结束的时间点
{
maxtime2[k][0]=maxtime2[k][0];
maxtime2[k][1]=maxtime[j+1][1];
j++;
}
if(maxtime[j][1]==finish[i]&&maxtime2[k][0]!=0&&maxtime[j+1][1]!=0)//该时间点有人为就餐结束的时间点
{
i++;
k++;
j++;
maxtime2[k][0]=maxtime[j][0];
maxtime2[k][1]=maxtime[j][1];
}
else if(maxtime[j][1]>=finish[i])
{
i++;
}
}
len3=k+1;
memset(maxtime,0,sizeof(maxtime));//初始化一个数组
i=0;
j=0;
maxtime[j][0]=maxtime2[i][0];
maxtime[j][1]=maxtime2[i][1];
for(i=0;i+1<len3;)//对得到的就餐人数最大的时间点再次进行计算简化
{
if(len3==1)
{
break;
}
if(maxtime2[i][1]-maxtime2[i+1][0]==-1)//相邻天数合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
i++;
}
else if(maxtime2[i][1]-maxtime2[i+1][0]==0)//同一天合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
i++;
}
else if(maxtime2[i+1][0]!=0)//不合并,向后移动一位
{
j++;
i++;
maxtime[j][0]=maxtime2[i][0];
maxtime[j][1]=maxtime2[i][1];
if(maxtime2[i][1]-maxtime2[i+1][0]==-1)//相邻天数合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
}
else if(maxtime2[i][1]-maxtime2[i+1][0]==0)//同一天合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
}
}
}
这个函数的源代码
//对起始时间和终止时间进行排序,并计算人数最多的时间
void sort(int start[], int finish[], int M, int N,int data[])
{
int i,j,k;
int min=0,len=0,len2=0,len3=0;
int maxnum=0;
for(i=0;i<M;i++)//复制start和finish数组
{
a1[i]=start[i];
a2[i]=finish[i];
}
merge(a1,start,min,M);//排序start数组
merge(a2,finish,min,M);//排序finish数组
for(j=0,i=M;j<M;i++,j++)//将start数组和finish数组合并
{
a1[i]=finish[j];
}
merge(a1,a2,min,2*M);//排序得到后的数组
i=0;
j=0;
c[j]=a2[i];//第一位赋值
i++;
while(i<2*M)//得到start和finish中出现的所有时间点,且不重复出现,并且有序
{
if(c[j]!=a2[i])
{
j++;
c[j]=a2[i];
}
i++;
}
len=j+1;
c[len]=c[len-1]+1;//多腾一个空间为后续计算时间段人数准备
len++;
for(i=0,j=0,k=0;i<2*M;i++)//计算哪个时间点有人进出
{
while(c[i]==start[j]&&j<M)
{
num[i]++;
j++;
}
while(c[i]==finish[k]&&k<M)
{
num[i+1]--;
k++;
}
}
data[0]=num[0];
for(i=1;i<len;i++)//计算每个时间点的人数
{
data[i]=num[i]+data[i-1];
}
for(i=0;i<len;i++)//寻找时间点人数最多的是多少人
{
if(maxnum<=data[i])
{
maxnum=data[i];
}
}
for(i=0,j=0;i<len;i++)//记录时间点人数最多的时间点
{
if(data[i]==maxnum)
{
maxtime[j][0]=c[i];
maxtime[j][1]=c[i];
j++;
}
}
len2=j;
i=0;
j=0;
k=0;
maxtime2[k][0]=maxtime[k][0];
maxtime2[k][1]=maxtime[k][1];
while(j+1<len2&&i<M)//判断是否在一个时间点到另一个时间点都是最大人数
{
if(len2==1)
{
break;
}
if(maxtime[j][1]<finish[i])//该时间点没人选择为就餐结束的时间点
{
maxtime2[k][0]=maxtime2[k][0];
maxtime2[k][1]=maxtime[j+1][1];
j++;
}
if(maxtime[j][1]==finish[i]&&maxtime2[k][0]!=0&&maxtime[j+1][1]!=0)//该时间点有人为就餐结束的时间点
{
i++;
k++;
j++;
maxtime2[k][0]=maxtime[j][0];
maxtime2[k][1]=maxtime[j][1];
}
else if(maxtime[j][1]>=finish[i])
{
i++;
}
}
len3=k+1;
memset(maxtime,0,sizeof(maxtime));//初始化一个数组
i=0;
j=0;
maxtime[j][0]=maxtime2[i][0];
maxtime[j][1]=maxtime2[i][1];
for(i=0;i+1<len3;)//对得到的就餐人数最大的时间点再次进行计算简化
{
if(len3==1)
{
break;
}
if(maxtime2[i][1]-maxtime2[i+1][0]==-1)//相邻天数合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
i++;
}
else if(maxtime2[i][1]-maxtime2[i+1][0]==0)//同一天合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
i++;
}
else if(maxtime2[i+1][0]!=0)//不合并,向后移动一位
{
j++;
i++;
maxtime[j][0]=maxtime2[i][0];
maxtime[j][1]=maxtime2[i][1];
if(maxtime2[i][1]-maxtime2[i+1][0]==-1)//相邻天数合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
}
else if(maxtime2[i][1]-maxtime2[i+1][0]==0)//同一天合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
}
}
}
len2=j+1;
printf("Result:\n");
for(j=0;j<len2;j++)//打印数据
{
printf("%d %d\n",maxtime[j][0],maxtime[j][1]);
}
}
#include
#include
#include
#include "string.h"
#define MAX 200002
int data[MAX];
int start[MAX], finish[MAX]; //分别存储起始时间和终止时间
int maxtime[MAX][2]={0},maxtime2[MAX][2]={0};
int a1[MAX]={0},a2[MAX]={0},c[MAX]={0},num[MAX]={0};
//录入学生登记的时间段,并判断输入是否合法
void registration(int start[], int finish[], int M, int N)
{
int i;
printf("Please enter the expected time period for each student in turn: \n");
for (i = 0; i < M; i++)//for循环进行输入
{
printf("The expected period of time for %d students: \n", i + 1);
scanf("%d", &start[i]);
scanf("%d", &finish[i]);
if(start[i]<0||finish[i]>N)//排除非法输入
{
assert(0);
}
}
}
void merge(int a[],int b[],int min,int max)//归并排序
{
int mid,p;//mid为数组的中间位置,p为从中间位置开始的指针
int min2;//记录了数组最小的位置
int i,j;
min2=min;//min为从最小位置开始的指针
if((max-min)<=1)//如果数组为1则return
{
return;
}
mid=(max+min)/2;//计算中间位置
p=mid;
merge(a,b,min,mid);//向中间位置的左边递归
merge(a,b,mid,max);//向中间位置的右边递归
for(i=min;i<max;)//开始归并排序
{
if(a[min]>a[p])//左边大于右边
{
b[i]=a[p];
p++;
if(p==max)//判断是否排序完成
{
i++;
for(j=min;j<mid;j++,i++)
{
b[i]=a[j];
}
}
else
{
i++;
}
}
if(a[min]<=a[p])//右边大于左边
{
b[i]=a[min];
min++;
if(min==mid)//判断是否排序完成
{
i++;
for(j=p;j<max;j++,i++)
{
b[i]=a[j];
}
}
else
{
i++;
}
}
}
for(i=min2;i<max;i++)//整理排序后的数组
{
a[i]=b[i];
}
}
//对起始时间和终止时间进行排序,并计算人数最多的时间
void sort(int start[], int finish[], int M, int N,int data[])
{
int i,j,k;
int min=0,len=0,len2=0,len3=0;
int maxnum=0;
for(i=0;i<M;i++)//复制start和finish数组
{
a1[i]=start[i];
a2[i]=finish[i];
}
merge(a1,start,min,M);//排序start数组
merge(a2,finish,min,M);//排序finish数组
for(j=0,i=M;j<M;i++,j++)//将start数组和finish数组合并
{
a1[i]=finish[j];
}
merge(a1,a2,min,2*M);//排序得到后的数组
i=0;
j=0;
c[j]=a2[i];//第一位赋值
i++;
while(i<2*M)//得到start和finish中出现的所有时间点,且不重复出现,并且有序
{
if(c[j]!=a2[i])
{
j++;
c[j]=a2[i];
}
i++;
}
len=j+1;
c[len]=c[len-1]+1;//多腾一个空间为后续计算时间段人数准备
len++;
for(i=0,j=0,k=0;i<2*M;i++)//计算哪个时间点有人进出
{
while(c[i]==start[j]&&j<M)
{
num[i]++;
j++;
}
while(c[i]==finish[k]&&k<M)
{
num[i+1]--;
k++;
}
}
data[0]=num[0];
for(i=1;i<len;i++)//计算每个时间点的人数
{
data[i]=num[i]+data[i-1];
}
for(i=0;i<len;i++)//寻找时间点人数最多的是多少人
{
if(maxnum<=data[i])
{
maxnum=data[i];
}
}
for(i=0,j=0;i<len;i++)//记录时间点人数最多的时间点
{
if(data[i]==maxnum)
{
maxtime[j][0]=c[i];
maxtime[j][1]=c[i];
j++;
}
}
len2=j;
i=0;
j=0;
k=0;
maxtime2[k][0]=maxtime[k][0];
maxtime2[k][1]=maxtime[k][1];
while(j+1<len2&&i<M)//判断是否在一个时间点到另一个时间点都是最大人数
{
if(len2==1)
{
break;
}
if(maxtime[j][1]<finish[i])//该时间点没人选择为就餐结束的时间点
{
maxtime2[k][0]=maxtime2[k][0];
maxtime2[k][1]=maxtime[j+1][1];
j++;
}
if(maxtime[j][1]==finish[i]&&maxtime2[k][0]!=0&&maxtime[j+1][1]!=0)//该时间点有人为就餐结束的时间点
{
i++;
k++;
j++;
maxtime2[k][0]=maxtime[j][0];
maxtime2[k][1]=maxtime[j][1];
}
else if(maxtime[j][1]>=finish[i])
{
i++;
}
}
len3=k+1;
memset(maxtime,0,sizeof(maxtime));//初始化一个数组
i=0;
j=0;
maxtime[j][0]=maxtime2[i][0];
maxtime[j][1]=maxtime2[i][1];
for(i=0;i+1<len3;)//对得到的就餐人数最大的时间点再次进行计算简化
{
if(len3==1)
{
break;
}
if(maxtime2[i][1]-maxtime2[i+1][0]==-1)//相邻天数合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
i++;
}
else if(maxtime2[i][1]-maxtime2[i+1][0]==0)//同一天合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
i++;
}
else if(maxtime2[i+1][0]!=0)//不合并,向后移动一位
{
j++;
i++;
maxtime[j][0]=maxtime2[i][0];
maxtime[j][1]=maxtime2[i][1];
if(maxtime2[i][1]-maxtime2[i+1][0]==-1)//相邻天数合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
}
else if(maxtime2[i][1]-maxtime2[i+1][0]==0)//同一天合并
{
maxtime[j][0]=maxtime[j][0];
maxtime[j][1]=maxtime2[i+1][1];
}
}
}
len2=j+1;
printf("Result:\n");
for(j=0;j<len2;j++)//打印数据
{
printf("%d %d\n",maxtime[j][0],maxtime[j][1]);
}
}
int main()
{
int N, M; //分别存储时间的段数和学生的个数
printf("Number of periods of time: ");
scanf("%d", &N);
printf("Number of students: ");
scanf("%d", &M);
getchar();
registration(start, finish, M, N); //录入学生登记的时间段
sort(start, finish, M, N, data); //对起始时间和终止时间进行排序
return 0;
}