题目一:邮局兴建问题
题目简述:
在一个小镇上准备要兴建邮局,需要把邮局建立在合适的位置,从而使得所有小镇居民去寄件和收取快递时行走的总路途最短。
(1)距离计算采用曼哈顿距离公式
distance(p1, p2) = |p2.x − p1.x| + |p2.y − p1.y|
(2)为了简化问题,允许邮局坐标和住户坐标重合
输入格式:
第一行为住户数目,第二行开始为每个住户的坐标
输出格式:
最短总距离
输入示例1:
3
0 0
0 4
2 2
输出示例1:
6
解释:
三个住户分别住在(0, 0), (0, 4), (2, 2),如图中的‘1’所标示的地方。
当把邮局建在点(0, 2)时,如图中的‘X’所标示的地方
最短的距离之和为2+2+2=6。
输入示例2:
6
0 2
0 4
1 0
1 2
1 4
2 2
输出示例2:
9
解释:
六个住户分别住在(0, 2), (0, 4), (1, 0),(1, 2), (1, 4), (2, 2)。
当把邮局建在点(1, 2)时,最短的距离之和为1+3+2+0+2+1=9。
题目二:邮递员年龄间隔
题目简述:
邮局建立后,准备招收若干邮递员。邮递员年龄在18岁到65岁之间。
现给出未排序的邮递员年龄的数组,求相邻年龄间隔的最大值。
Tips: 邮递员数量小于等于1000
Tips: 要求时间复杂度和空间复杂度都为O(n)
输入示例:
4
23 46 29 41
输出示例:
12
解释:
排序后为[23, 29, 41, 46],最大间隔值为41-29=12。
题目三:街道归属问题
题目简述:
一个邮递员负责一个街道的派送,一个街道由一个或一个以上的邮递员负责。
现在向邮递员提问,邮递员不清楚其他街道的情况,只能回答有多少个邮递员和他负责同一条街道。不一定所有邮递员都会回答。根据邮递员的回答推断邮局最少有多少名邮递员。
Tips: 邮递员数量小于等于1000
输入格式:
第一行为参与回答的邮递员数目n,第二行为n个正整数,空格间隔。
输出格式:
正整数,表示最少的邮递员数目。
输入示例1:
3
1 1 2
输出示例1:
5
解释:
两个邮递员说有1名同事和自己负责同一个街道,可以认为这两个邮递员属于同一个街道;
第三名邮递员说有两名同事和自己负责同一个街道,说明他和前两名邮递员不负责同一条;
所以邮局至少有5名邮递员。
输入示例2:
4
1 1 2 2
输出示例2:
5
解释:
两个邮递员说有1名同事和自己负责同一个街道,可以认为这两个邮递员属于同一个街道;
后面两名邮递员说有两名同事和自己负责同一个街道,说明他们和前两名邮递员不负责同一条;
所以邮局至少有5名邮递员。
简要解析
第一题考察的主要知识点就是排序。只需分别对所有的x坐标和y坐标进行排序,然后根据住户的数量为奇数还是偶数选择有序数组相应的中间值即可。
第二题考察的主要知识点也是排序,只需将邮递员的年龄进行排序,然后依次计算相邻快递员年龄的差并取最大值即可。
第三题考察的主要知识点也是排序,解题难度稍大。可以将快递员同事数量按照非递减的顺序排列,之后可以分为以下三种情况:
①连续n+1个快递员都说自己有n个同事,那么这n+1个快递员属于同一街道。
比如2 2 2,那么这三个快递员可以分配在同一街道。
②连续k个快递员都说自己有n个同事(k<n+1),那么这k个快递员属于同一街道,并且需要补上没有回答的n-k+1个快递员。
比如2 2,那么这两个快递员可以分配在同一街道,并且该街道有1名快递员没有参与回答。
③连续k个快递员都说自己有n个同事(k>n+1),那么可以每次把连续的n+1个快递员按第一种方式处理,把最后剩下的s个快递员按第二种方式处理(s<n+1)。
求解代码
#include <stdio.h>
#include <stdlib.h>
#define MaxNum 1000//邮递员数量、住户数量的最大值
#define MinNum 0//邮递员数量、住户数量的最小值
#define MaxAge 65//邮递员年龄的最大值
#define MinAge 18//邮递员年龄的最小值
#define MAX(a,b) ((a>b) ? a : b)//返回a、b中的较大值
#define MIN(a,b) ((a<b) ? a : b)//返回a、b中的较小值
//交换数组中相应区段元素的位置,使枢轴元素到位,并返回其所在位置,此时在它前(后)面的元素均不大(小)于它
int Partition(int nums[], int low, int high)
{
int pivotkey;//枢轴
pivotkey = nums[low];
while(low < high)
{
while((low < high) && (nums[high] >= pivotkey))high--;//找到比枢轴元素小的元素
nums[low] = nums[high];//将小的元素换到低端
while((low < high) && (nums[low] <= pivotkey))low++;//找到比枢轴元素大的元素
nums[high] = nums[low];//将大的元素换到高端
}
nums[low] = pivotkey;//枢轴元素到位
return low;//返回枢轴元素所在位置
}
//快速排序的递归形式,将数组中相应区段的元素按非递减的顺序排列
void QSort(int nums[], int low, int high)
{
int pivotkey;//枢轴
if(low < high)
{
pivotkey = Partition(nums, low, high);//枢轴元素的位置
QSort(nums, low, pivotkey-1);//对子数组进行快速排序
QSort(nums, pivotkey+1, high);//对子数组进行快速排序
}
}
//快速排序算法
void QuickSort(int nums[], int numsSize)
{
QSort(nums, 0, numsSize-1);//调用函数对数组进行排序
}
//求解邮局与所有住户之间的最小总距离
int MinTotalDistance(int X[], int Y[], int n)
{
int distance = 0;//初始最小总距离为0
int i, temp;
if(!n)//住户数量为0
return -1;
QuickSort(X, n);//将x坐标数组按非递减顺序排列
QuickSort(Y, n);//将y坐标数组按非递减顺序排列
if(n%2)
temp = (n-1)/2;//n为奇数时有序数组中间值的下标,即满足取得最小距离的邮局的位置的x坐标和y坐标在数组中的下标
else
temp = n/2;//n为偶数是有序数组中间值的下标,即满足取得最小距离的邮局的位置的x坐标和y坐标在数组中的下标
for(i = 0; i < n; i++)
{
distance += (abs(X[temp] - X[i]) + abs(Y[temp] - Y[i]));//最小总距离等于邮局和所有住户之间x坐标及y坐标的差值的绝对值之和
}
return distance;
}
//求解按年龄排列的相邻两个邮递员之间的最大年龄差
int MaximumGap(int nums[], int numsSize)
{
int i, Max = 0;//初始最大年龄差为0
if(numsSize == 0)//邮递员数量为0
return -2;
if(numsSize == 1)//当只有一个邮递员时,最大年龄差为-1
return -1;
QuickSort(nums, numsSize);//将年龄数组按非递减顺序排列
for(i = 0; i < numsSize-1; i++)
{
Max = MAX((nums[i+1] - nums[i]), Max);//不断更新最大年龄差
}
return Max;
}
//求解邮局中邮递员数量的最小值
int GetMinPostman(int nums[],int n)
{
int pNumber=0;//初始数量为0
int i, j, temp, flag;//flag标志能否将邮递员安排在同一街道
if(!n)//参与回答的邮递员的数量为0
return -2;
QuickSort(nums, n);//将同事数量数组按非递减顺序排列
for(i = 0; i < n; )//遍历同事数量数组
{
flag = 1;
temp = nums[i];//同事数量
if(temp)//同事数量大于0
{
for(j = 1; j <= temp; j++)//遍历当前元素之后的temp个元素,判断能否将其安排在同一街道
{
if(nums[i+j] != temp)//存在同事数量不等的元素
{
flag = 0;//不能将其安排在同一街道
break;//停止遍历
}
}
}
if(flag)//邮递员可以安排在同一街道
{
pNumber += (temp + 1);//邮递员数量增加temp+1个,即该街道有temp+1个邮递员
i += (temp + 1);//下一遍历位置 = 当前遍历位置 + temp + 1,即跨过同一街道中的temp+1个邮递员
}
else//邮递员不能安排在同一街道
{
pNumber += (j * (temp + 1));//j表示邮递员数量相等的街道的个数,且每个街道有temp+1个邮递员
i += j;//下一遍历位置 = 当前遍历位置 + j,即跨过j个含有相同数量的邮递员的街道
}
}
return (pNumber > MinNum && pNumber <= MaxNum)? pNumber : -1;
}
//输入数据方式
void Input(char *choice)
{
while(getchar() != '\n')continue;
printf("\n1.Manual input 2.File input else.Quit\n");
printf("Input your choice: ");
scanf("%c", choice);
}
//重新选择功能
void Continue(char *order)
{
while(getchar() != '\n')continue;
printf("\nDo you want to reselect a function(Y/N)? ");
scanf("%c", order);
while(getchar() != '\n')continue;
printf("\n");
}
//邮局与所有居民之间的最小总距离
char Task_one()
{
int n, count = 1;//n表示住户的数量
char choice, order = 'N';
Input(&choice);//选择输入数据的方式
switch (choice)
{
case '1':
while(getchar() != '\n')continue;
printf("\nInput the number of households(%d-%d,else to quit): ", MinNum, MaxNum);
while(scanf("%d", &n) && n >= MinNum && n <= MaxNum)
{
count = 1;
int X[n], Y[n];//坐标数组,用于保存住户的x坐标和y坐标
for(int i = 0; i < n; i++)
{
while(getchar() != '\n')continue;
printf("Input the No.%02d household coordinates(x,y): ", count++);
scanf("%d,%d", &X[i], &Y[i]);
}
printf("The minimum total distance is %d.\n", MinTotalDistance(X, Y, n));//调用函数求解最小总距离
printf("\nInput the number of households(%d-%d,else to quit): ", MinNum, MaxNum);
}
Continue(&order);
break;
case '2':
printf("\n(Filename: 5_1_input.in)\nThe results are as follows:\n");//同步修改文件名
freopen("5_1_input.in", "r", stdin);
while(scanf("%d", &n) != EOF && n >= MinNum && n <= MaxNum)
{
int X[n], Y[n];//坐标数组,用于保存住户的x坐标和y坐标
for (int i = 0; i < n; i++)
{
if(scanf("%d%d", &X[i], &Y[i]) == EOF)//文件数据不足
{
printf("Insufficient data.Please check the data in the file.\n");
exit(0);
}
}
printf("Case %02d: The minimum total distance is %d.\n", count++, MinTotalDistance(X, Y, n));//调用函数求解最小总距离
}
fclose(stdin);
break;
default:
Continue(&order);
break;
}
return order;
}
//按年龄排列的相邻两个邮递员之间的最大年龄差MinAge 18//邮递员年龄的最小值
char Task_two()
{
int n;//邮递员数量
int count = 1;
char choice, order = 'N';
Input(&choice);//选择输入数据的方式
switch (choice)
{
case '1':
while(getchar() != '\n')continue;
printf("\nInput the number of postmen(%d-%d,else to quit): ", MinNum, MaxNum);
while(scanf("%d", &n) && n >= MinNum && n <= MaxNum)
{
count = 1;
int nums[n];//年龄数组,用于保存邮递员的年龄
for(int i = 0; i < n; i++)
{
while(getchar() != '\n')continue;
printf("Input the age of the No.%02d postman(%d-%d): ", count++, MinAge, MaxAge);
while(!scanf("%d", &nums[i]) || nums[i] < MinAge || nums[i] > MaxAge)
{
while(getchar() != '\n')continue;
printf("Out of range.Input again: ");
}
}
printf("The maximum age difference is %d.\n", MaximumGap(nums, n));//调用函数求解最大年龄差
printf("\nInput the number of postmen(%d-%d,else to quit): ", MinNum, MaxNum);
}
Continue(&order);
break;
case '2':
printf("\n(Filename: 5_2_input.in)The results are as follows:\n");//同步修改文件名
freopen("5_2_input.in", "r", stdin);
while(scanf("%d", &n) != EOF && n >= MinNum && n <= MaxNum)
{
int nums[n]; //快递员年龄数组
for(int i = 0;i < n;i++)
{
while(scanf("%d", &nums[i]) == EOF || nums[i] < MinAge || nums[i] > MaxAge)
{
if(scanf("%d", &nums[i]) == EOF)//文件数据不足
{
printf("Insufficient data.Please check the data in the file.\n");//文件数据不足
exit(0);
}
}
}
printf("Case %02d: The maximum age difference is %d.\n", count++, MaximumGap(nums, n));//调用函数求解最大年龄差
}
fclose(stdin);
break;
default:
Continue(&order);
break;
}
return order;
}
//邮局中邮递员数量的最小值
char Task_three()
{
int n, i, count = 1;//n表示参与回答的邮递员的数量
char choice, order = 'N';
Input(&choice);//选择输入数据的方式
switch (choice)
{
case '1':
while(getchar() != '\n')continue;
printf("\nInput the number of the answering postmen(>0,else to quit): ");
while(scanf("%d", &n))
{
count = 1;
int nums[n];//同事数量数组,用于保存邮递员所在街道内的同事数量
for(i = 0; i < n; i++)
{
while(getchar() != '\n')continue;
printf("Input the answer of the NO.%02d postman: ", count++);
while(!scanf("%d", &nums[i]) || nums[i] < MinNum || nums[i] >= MaxNum)
{
while(getchar() != '\n')continue;
printf("Out of range.Input again: ");
}
}
printf("(Effective range: %d-%d)The minimum number of postmen is %d.\n", MinNum, MaxNum, GetMinPostman(nums, n));//调用函数求解邮递员的最小数量
printf("\nInput the number of the answering postmen(>0,else to quit): ");
}
Continue(&order);
break;
case '2':
printf("\n(Filename: 5_3_input.in)The results are as follows:\n");//同步修改文件名
freopen("5_3_input.in", "r", stdin);
while(scanf("%d", &n) != EOF)
{
int nums[n];//同事数量数组,用于保存邮递员所在街道内的同事数量
for(i = 0; i < n; i++)
{
if(scanf("%d", &nums[i]) == EOF || nums[i] < MinNum || nums[i] >= MaxNum)
{
if(scanf("%d", &nums[i]) == EOF)//文件数据不足
{
printf("Insufficient data.Please check the data in the file.\n");//文件数据不足
exit(0);
}
}
}
printf("Case %02d: (Effective range: %d-%d)The minimum number of postmen is %d.\n", count++, MinNum, MaxNum, GetMinPostman(nums, n));//调用函数求解邮递员的最小数量
}
fclose(stdin);
break;
default:
Continue(&order);
break;
}
return order;
}
int main()
{
char choice, order;
do{
printf("1.Get the minimum total distance between the post office and all residents\n");
printf("2.Get the maximum age difference between two adjacent postmen arranged by age\n");
printf("3.Get the minimum number of postmen in the post office else.Quit\n");
printf("Input your choice: ");
scanf("%c", &choice);//选择功能
switch (choice)
{
case '1':
order = Task_one();
break;
case '2':
order = Task_two();
break;
case '3':
order = Task_three();
break;
default:
exit(0);
}
}while(order == 'Y' || order == 'y');//Y或y表示重新选择功能
return 0;
}
敲黑板!!!排序和搜索非常常用,需要清楚地知道每一种排序方法和搜索方法的逻辑、代码、时间复杂度、空间复杂度等。