Case04——区间选点问题
原始问题:
有若干的区间里,找最少的点包括所有区间,
思路:依此选取右边的点。
—1—x ——4——
——2—— ——3——x
————————————
如图可以选取1号最右边的点,3号区间最右边的点,这样我们选择的点就是最优的。
变体
POJ 1201
Description
You are given n closed, integer intervals [ai, bi] and n integers c1, ..., cn.
Write a program that:
reads the number of intervals, their end points and integers c1, ..., cn from the standard input,
computes the minimal size of a set Z of integers which has at least ci common elements with interval [ai, bi], for each i=1,2,...,n,
writes the answer to the standard output.
Input
The first line of the input contains an integer n (1 <= n <= 50000) -- the number of intervals. The following n lines describe the intervals. The (i+1)-th line of the input contains three integers ai, bi and ci separated by single spaces and such that 0 <= ai <= bi <= 50000 and 1 <= ci <= bi - ai+1.
Output
The output contains exactly one integer equal to the minimal size of set Z sharing at least ci elements with interval [ai, bi], for each i=1,2,...,n.
Sample Input
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
Sample Output
6
中文:
给出n个闭整数区间[ai,bi]和n个整数C1,.,cn。
编写一个程序:
从标准输入读取间隔的数目、它们的端点和整数c1、.、cn,
计算具有区间[ai,bi]的至少ci公共元素的整数集Z的最小大小,对于每一个i=1,2,.,n,
将答案写入标准输出。
输入
输入的第一行包含整数n(1<=n<=50000)-间隔数。下面n行描述间隔。输入的(i+1)-第一行包含由单个空格分隔的三个整数ai、bi和ci,使得0<=ai<=bi<=50000和1<=ci<=bi-ai+1。
输出量
输出包含一个整数,等于集合Z的最小大小,对于每一个i=1,2,.,n至少具有区间[ai,bi]的Ci元素。
样本输入
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
样本输出
6
基本解决思路:(AC不了,会超时)
依次选取右边的点,并用一个数组记录已经选点的情况,标记这点值为1,(其他的为0)这时会有3种情况:
1,少
依次往右扫点,直到满足条件。
2,多
没影响,代码你会看到,它满足条件,进不去循环。
3,正好
代码如下:
package greed_dynamic;
import static java.lang.Math.min;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
/*
Sample Input
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
Sample Output
6
*/
public class Pra {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
interval[] intervals = new interval[n];
for (int i = 0; i < intervals.length; i++) {
intervals[i] = new interval(sc.nextInt(),sc.nextInt(),sc.nextInt());
}
Arrays.sort(intervals);
int max = intervals[n - 1].t;//最右边的点
int[] axis = new int[max+1];
for (int i = 0; i < intervals.length; i++) {
int s = intervals[i].s;
int t = intervals[i].t;
int c = intervals[i].c;
int cnt = sum(axis,s,t);
c -= cnt;
while(c > 0) {
if(axis[t] == 0) {//如果为0,则为没有标记过的,然后c大于0,代表还有点未选
//所以继续向右选
axis[t] = 1;//标记
t--;
c--;
} else {//标记的有,就跳过
t--;
}
}
}
System.out.println(sum(axis, 0, max));
}
//记录axis数组的被标记的点
private static int sum(int[] axis, int s, int t) {
// TODO 自动生成的方法存根
int sum = 0;
for (int i = s; i < t; i++) {
sum += axis[i];
}
return sum;
}
public static class interval implements Comparable<interval>{
int s;
int t;
int c;
public interval (int s,int t,int c) {
this.s = s;
this.t = t;
this.c = c;
}
public int compareTo(interval other) {
// TODO 自动生成的方法存根
int x = this.t - other.t;
if (x == 0) {
return this.s - other.s;
}
return x;
}
}
}
AC需要接入一个·新的知识——树状数组
这里奉上两个大佬的博客,很详细,结合着看
https://www.cnblogs.com/xenny/p/9739600.html
https://www.cnblogs.com/findview/archive/2019/08/01/11281628.html
模板 树状数组代码如下:
package greed_dynamic;
import Sort.Util;
public class 树状数组 {
static int n;
static int[] a ;//原数组
static int[] c ;//树状数组
public static void main(String[] args) {
// TODO 自动生成的方法存根
int[] a = {1,2,3,4,5,6};
n = a.length;
c = new int[a.length+1];
for (int i = 0; i < a.length; i++) {
updata(i + 1, a[i]);
}
Util.printArr(c);
}
//核心所在,详细看看博客,这里不再赘述
public static int lowBit(int x) {
return x&(-x);//求2^k
}
/*单点更新
* 注意这里不是一个一个求的,如在一个长度为8的数组中,A[2] 被c[2],c[4],c[8]
* 即A[i] 包含于 C[i + 2k]、C[(i + 2k) + 2k]...;
* 对于给出的例子x2,假设数组下标上限n8,x转换成二进制后等于0010(C2),
* 对末尾1的位置进行+1,得到0100(C4),对末尾的1的位置进行+1,得到1000(C8),
* 循环结束,对C2,C4,C8的前缀和都要加上value,当然不能忘记对A[2]的值+value,单点更新值过程结束
*/
public static void updata(int i,int k) { //在i位置加上k
while (i <= n) {
c[i] += k;
i+=lowBit(i);
}
}
/*
* 求前缀和,13 & (-13) = 1
* 13 - 1 = 12
* 12 & (-12) = 4
* 12 - 4 = 8
* 8 & (-8) = 8
* 8 - 8 =0
* 结束循环,所以前13项和为C[13]+c[12]+c[8]
*/
public static int getSum(int i) { //求A[1 - i]的和
int res = 0;
while(i > 0) {
res += c[i];
i -= lowBit(i);
}
return res;
}
}
AC代码:
package greed_dynamic;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
/*
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
*/
public class Case04_区间选点II {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
interval[] intervals = new interval[n];
for (int i = 0; i < n; i++) {
intervals[i] = new interval(sc.nextInt(), sc.nextInt(),sc.nextInt());
}
Arrays.sort(intervals);
int max = intervals[n-1].t;
int[] axis = new int[max+1];
int[] c_tree = new int[max+2];//树状数组从1开始,为防止越界
for (int i = 0; i < n; i++) {
int s = intervals[i].s;
int t = intervals[i].t;
int c = intervals[i].c;
int cnt = getSum(t+1,c_tree,max+1) - getSum(s,c_tree,max+1);//从t到s-1这个区间,记住树状数组从1开始
c -= cnt;
while (c > 0) {
if (axis[t] == 0) {
axis[t] = 1;
update(t+1, 1, c_tree, max + 2);//t+1代表axis[t],每次加1,这里有点绕
t--;
c--;
} else {
t--;
}
}
}
System.out.println(getSum(max+2, c_tree, max+1));
}
private static int lowBit(int x) {
return x & (-x);
}
private static void update(int i,int k,int[] c,int n) {
while(i <=n ) {
c[i] += k;
i += lowBit(i);
}
}
private static int getSum(int i, int[] c_tree, int n) {
// TODO 自动生成的方法存根
int res = 0 ;
if (i > n)//max+2是为防止越界,没有实际含义
i = n;
while(i > 0) {
res += c_tree[i];
i -= lowBit(i);
}
return res;
}
public static class interval implements Comparable<interval>{
int s;
int t;
int c;
public interval(int s,int t,int c) {
this.s = s;
this.t = t;
this.c = c;
}
@Override
public int compareTo(interval other) {
// TODO 自动生成的方法存根
int x = this.t - other.t;
if (x == 0) {
return this.s - other.s;
}
return x;
}
}
}
精力有限,难免有不足,不会请评论或私信