再探贪心之区间选点问题

再探贪心

问题回顾

初始贪心的节目分配问题可以抽象为多个区间不相交问题,区间选点问题是进一步的变式。

问题呈现

问题描述

给定n个闭区间,问最少需要确定多少个点,才能使每个闭区间中都至少存在一个点。

输入描述

第一行为一个正整数n,表示闭区间的个数。

接下来n行,每行两个正整数x,y,分别表示第i个闭区间的左端点和右端点。

输出描述

输出一个整数,表示最少需要确定的点的个数。

样例1

输入
3
1 4
2 6
5 7
输出
2

问题分析

抽象呈现

将问题中的区间抽象为不同颜色的线的范围,黑色部分为重合部分(可选点)虚线左右两侧为被区分的区间(6个区间被分为3个一组)

问题解决

根据贪心算法的解决步骤,我们需要找到每个情况下的最优解。

  1. 找到最小的右端点作为第一个讨论对象。进行第一次分组(左端点小于这个右端点的分为一组,左端点大于这个右端点的分为一组)
  2. 我们清楚发现,左边的一组即为一点可覆盖区间(第一次分组下的最大覆盖)
  3. 对右边一组进行处理,重复第一个步骤,即找到剩下所有区间的最小右端点,重复进行分组。

代码呈现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int n = 0;                    //定义全局变量,便于后续对所有区间进行处理
int repeat(int N[][2], int n, int* min);

int main() {
	int N[100][2];
	int a = 0;
	int* min=&a;              //定义指针,储存每一次分组后的最小右端点
	int t = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%d%d", &N[i][0], &N[i][1]);
	*min = N[0][1];           
	for (int i = 1; i < n; i++)
		if (N[i][1] < *min)
			*min = N[i][1];     //找到第一个最小右端点
    while(repeat(N, n, min))    //进行分组处理
		t++;
	printf("%d", t+1);        //最后一组因为直接判断退出,需要+1
}

int repeat(int N[][2], int n, int* min) {
	int M[100];
	int k = 0;
	int flag = 1;
	for (int i = 0; i < n; i++) {         
		if (N[i][0] <= *min)              //根据左端点与最小右端点比较进行分组
		{
			n--;
			
			for (int j = i; j < n; j++) {  //将被分在左侧的区间从数组中去除
				N[j][0] = N[j + 1][0];
				N[j][1] = N[j + 1][1];
			}
			i--;
		}
		else
		{
			M[k++] = i;                   //判断能否继续分组
			flag = 0;
		}
	}
	if (flag)                             //判断循环继续条件
		return 0;
	*min = N[M[0]][1];
	for (int i = 1; i < k; i++) {         //找到右侧一组的最小右端点
		if (N[M[i]][1] < *min)
			*min = N[M[i]][1];
	}
	return 1;
}

总结反思 

通过区间问题的变式,更深入地了解贪心算法。

在敲代码的过程中,出现了问题——开始时我的指针不是储存最小右端点的值,而是指向最小右端点的地址,导致去除左侧分组区间时指针指向错误,通过储存值解决了这个问题。

在敲代码的注释过程中,我发现这个代码还有漏洞,不知道真正理解的大家能否找到这个小错误?

欢迎大家交流和指出代码问题!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值