再探贪心
问题回顾
初始贪心的节目分配问题可以抽象为多个区间不相交问题,区间选点问题是进一步的变式。
问题呈现
问题描述
给定n个闭区间,问最少需要确定多少个点,才能使每个闭区间中都至少存在一个点。
输入描述
第一行为一个正整数n,表示闭区间的个数。
接下来n行,每行两个正整数x,y,分别表示第i个闭区间的左端点和右端点。
输出描述
输出一个整数,表示最少需要确定的点的个数。
样例1
输入
3
1 4
2 6
5 7
输出
2
问题分析
抽象呈现
将问题中的区间抽象为不同颜色的线的范围,黑色部分为重合部分(可选点)虚线左右两侧为被区分的区间(6个区间被分为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;
}
总结反思
通过区间问题的变式,更深入地了解贪心算法。
在敲代码的过程中,出现了问题——开始时我的指针不是储存最小右端点的值,而是指向最小右端点的地址,导致去除左侧分组区间时指针指向错误,通过储存值解决了这个问题。
在敲代码的注释过程中,我发现这个代码还有漏洞,不知道真正理解的大家能否找到这个小错误?
欢迎大家交流和指出代码问题!