最大重叠区间问题应用

1.介绍最大重叠区间问题
最大重叠问题就是给出若干个区间分别的首末位置,然后要求输出他们重叠最多的区间,有点类似于取并集。也就是求出一个区间,这个区间在k个输入区间之内,且k取最大并且这个区间长度要尽量长。
举一个例子方便理解,如下图:

输入区间的起点和终点分别按顺序用两个数组来接收。我们需要做的是,实现一个通过传入这两个数组的信息找到这些区间之间重叠次数最多的部分并打印。
首先有一个思路就是我们需要将输入的数据按照上面画的图一样,在一个横轴上摊开,这样我们才可以确定在哪里是重叠部分最大的地方。而这里的摊开实际上就是为每个区点的边界点确定一个合适的位置,其实说白了就是给这些起点终点进行排序。
但仅仅直接排序似乎又有问题。因为如果不加处理直接把这些点排成一个序列的画,我们根本无法确定谁是终点谁是起点,所以我们必须为这些输入的点设置一个标记来判断是区间起点还是区间端点。
作好了这些准备工作后,我们就可以在一串按顺序排列的起点终点序列中判断出最大重叠区间了。我们需要先重新看待起点代表的含义,认为遇到一个起点也就是说明了进入了一个某个区间之内了。尽管这样子我们不知道这个起点对应哪个区间,但当我们在考虑最大重叠区间的时候,由于我们不关心到底具体是哪些个区间重叠在一起,只是关心重叠次数,所以这样子的理解便足以解决问题了。
由上面的讨论可以知道,我们找最大重叠区间的过程也就是顺着点序列看是否经过了起点的过程,因为经过一个起点意味着我们来到了一个区间。同理我们也需要将遇到终点看成是走出了某一个区间。
故可以统计当前进入的区间数量,遇到起点加一,遇到终点减一,然后找到过程中令该值最大时加上的起点就可以了。又因为在该起点处值是最大的,所以之后遇到的点必然是区间的终点,也就是走出了这个让进入区间数量最大的区域。这样便确定了最大的重叠区间。
所以针对这个问题的代码实现如下(输入格式:第一行输入区间数量,第二行开始每行输入一个区间起点、终点并用空格隔开):

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

void quickSort(int *a, int n);

void IntervalCovered(int* start, int* end, int n)
{
	//归并前两个数组进行排序
	quickSort(start, n);
	quickSort(end, n);
	//设置一个数组作为归并后的结果
	int* start_and_end = (int*)malloc(sizeof(int)* 2 * n);
	int* isStart = (int*)malloc(sizeof(int)* 2 * n);
	int begin1 = 0, begin2 = 0;
	int i = 0;
	while (begin1<n&&begin2<n)
	{
		if (start[begin1]>end[begin2])
		{
			isStart[i] = 0;
			start_and_end[i++] = end[begin2++];
		}
		else{
			//注意,必须要等于的时候先放入start中的元素才可以
			isStart[i] = 1;
			start_and_end[i++] = start[begin1++];
		}
	}
	while (begin1<n)
	{
		isStart[i] = 1;
		start_and_end[i++] = start[begin1++];
	}
	while (begin2<n)
	{
		isStart[i] = 0;
		start_and_end[i++] = end[begin2++];
	}
	//计算重复区间的最大次数
	int count = 0, max = 0;
	int* result = (int*)malloc(sizeof(int)*n);
	for (int i = 0; i < 2 * n; i++)
	{
		if (isStart[i])
		{
			count++;
			if (count>max)
			{
				max = count;
			}
		}
		else
		{
			count--;
		}
	}
	//从上面循环出来后count值为0
	//设置cur用于保存下面找到的区间起点下标所应该放的位置
	int cur = 0;
	//比较若与重复次数相同则就是最大重复区间
	for (int i = 0; i < 2 * n; i++)
	{
		if (isStart[i])
		{
			count++;
			if (count == max)
			{
				result[cur++] = i;
			}
		}
		else
		{
			count--;
		}
	}

	for (int i = 0; i < cur; i++)
	{
		printf("[%d,%d]",start_and_end[result[i]],start_and_end[result[i]+1]);
		if (i + 1 == cur)
		{
			printf("\n");
		}
	}
	free(result);
	free(isStart);
	free(start_and_end);
}

int main()
{
	int start, end;
	int *_start,* _end;
	int n;
	scanf("%d", &n);
	_start = (int*)malloc(sizeof(int)*n);
	_end = (int*)malloc(sizeof(int)*n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d %d", &start, &end);
		_start[i] = start;
		_end[i] = end;
	}
	IntervalCovered(_start, _end, n);
	free(_start);free(__end);
	return 0;
}

//后面是快排的实现,不多赘述
int getMid(int *a, int n)
{
	int left = 0, right = n - 1, mid = (n - 1) / 2;
	if (a[left]<a[mid])
	{
		if (a[mid]<a[right])
			return mid;
		else{
			if (a[left]<a[right])
				return right;
			else
				return left;
		}
	}
	//a[left]>a[mid]
	else{
		if (a[mid]>a[right])
			return mid;
		else{
			if (a[left]>a[right])
				return right;
			else
				return left;
		}
	}
}

void swap(int*a, int*b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int partion(int* a, int n)
{
	int left = 0;
	int right = n - 1;
	int key = a[0];
	while (left<right)
	{
		while (left<right&&a[right]>key)
		{
			right--;
		}
		if (left<right)
		{
			a[left++] = a[right];
		}
		while (left<right&&a[left]<key)
		{
			left++;
		}
		if (left<right)
		{
			a[right--] = a[left];
		}
	}
	a[right] = key;
	return right;
}
void quickSort(int* a, int n)
{
	if (n>1)
	{
		int k = getMid(a, n);
		swap(&a[0], &a[k]);
		//对数组进行划分
		//返回最后的位置
		int pos = partion(a, n);
		quickSort(a, pos);
		quickSort(a + pos + 1, n - pos - 1);
	}
}

输入:
在这里插入图片描述
输出结果:
在这里插入图片描述
2. 应用问题描述
来自学校里面的一道实验题:
在这里插入图片描述
举例说明:

本题其实就是最大重叠区间问题的变形。输入的每个区间代表一个学生有空闲的起始和终止时间段,然后输出结果就是重叠最多的区间(重叠次数实际上就是该区间代表的时间段内有空闲的学生人数),输出该区间的起始和终止时间段,因为可能存在重叠次数一样是最多的区间,所以需要把所有次数一样的给打印出来。
这样就和我们上面介绍的最大重叠问题完全一模一样了,于是我贴一模一样的代码应该没有关系:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void quickSort(int* a, int n);

int *isBegin, *start_and_end;
//对起始时间与终止时间进行排序
void sort(int *start, int *end, int M){
	isBegin = (int*)malloc(sizeof(int)*M * 2);
	start_and_end = (int*)malloc(sizeof(int)*M * 2);
	quickSort(start, M); quickSort(end, M);
	int begin1 = 0, begin2 = 0;
	int i = 0;
	while (begin1<M&&begin2<M)
	{
		if (start[begin1]>end[begin2])
		{
			isBegin[i] = 0;
			start_and_end[i++] = end[begin2++];
		}
		else{
			isBegin[i] = 1;
			start_and_end[i++] = start[begin1++];
		}
	}
	while (begin1<M)
	{
		isBegin[i] = 1;
		start_and_end[i++] = start[begin1++];
	}
	while (begin2<M)
	{
		isBegin[i] = 0;
		start_and_end[i++] = end[begin2++];
	}
}

//计算空闲人数最多的起始时间和终止时间
void findPeriod(int *start, int *end, int M, int N){
	int count = 0, max = 0;
	int *last = malloc(sizeof(int)*M);
	for (int i = 0; i < 2 * M; i++)
	{
		if (isBegin[i]){
			count++;
			if (count>max)
			{
				max = count;
			}
		}
		else{
			count--;
		}
	}
	count = 0; int k = 0;
	for (int i = 0; i < 2 * M; i++)
	{
		if (isBegin[i]){
			count++;
			if (count == max)
			{
				last[k++] = i;
			}
		}
		else{
			count--;
		}
	}
	//出来的last就是最大的last
	for (int i = 0; i<k; i++)
	{
		printf("%d %d", start_and_end[last[i]], start_and_end[last[i] + 1]);
		if (i != k - 1)
		{
			printf(",");
		}
	}
	printf("\n");
	free(isBegin); free(start_and_end); free(last);
}

int main()
{
	int N, M; //分别存储时间的段数和学生的个数
	int caseNum = 0;
	int start[1002];
	int end[1002];

	if (freopen("5_3_input.in", "r", stdin) == NULL) {
		printf("There is an error in reading file 5_3_input.in");
	}

	while (scanf("%d %d", &N, &M) != EOF){
		caseNum++;
		printf("==== Case %d ====\n", caseNum);
		for (int i = 0; i < M; i++){
			scanf("%d %d", &start[i], &end[i]);
		}
		sort(start, end, M);
		findPeriod(start, end, M, N);
	}
	fclose(stdin);
}

int getMid(int *a, int n)
{
	int left = 0, right = n - 1, mid = (n - 1) / 2;
	if (a[left]<a[mid])
	{
		if (a[mid]<a[right])
			return mid;
		else{
			if (a[left]<a[right])
				return right;
			else
				return left;
		}
	}
	//a[left]>a[mid]
	else{
		if (a[mid]>a[right])
			return mid;
		else{
			if (a[left]>a[right])
				return right;
			else
				return left;
		}
	}
}

void swap(int*a, int*b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int partion(int* a, int n)
{
	int left = 0;
	int right = n - 1;
	int key = a[0];
	while (left<right)
	{
		while (left<right&&a[right]>key)
		{
			right--;
		}
		if (left<right)
		{
			a[left++] = a[right];
		}
		while (left<right&&a[left]<key)
		{
			left++;
		}
		if (left<right)
		{
			a[right--] = a[left];
		}
	}
	a[right] = key;
	return right;
}
void quickSort(int* a, int n)
{
	if (n>1)
	{
		int k = getMid(a, n);
		swap(&a[0], &a[k]);
		//对数组进行划分
		//返回最后的位置
		int pos = partion(a, n);
		quickSort(a, pos);
		quickSort(a + pos + 1, n - pos - 1);
	}
}


然后这是输入文件5_3_input.in的输入内容:

输出结果:
在这里插入图片描述

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值