Case04——区间选点问题,POJ1201详细题解

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;
		}

		
		
	}

}

精力有限,难免有不足,不会请评论或私信

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值