俄国沙皇问题笔记

给定一个N*2的二维数组,看作是一个个二元组,[a1,b1],[a2,b2],[a3,b3],规定:如果想把一个二元组甲放到二元组乙上,甲中的a值必须大于乙中的a值,甲中的b值必须大于乙中的b值,如果二维数组中随意选择二元组,请问二元组中最多可以往上放多少个?
例如:[[7,8],[5,9],[1,2],[6,4],[8,6]],则最多可以放三个:[1,2]->[6,4]->[7,8]或[1,2]->[6,4]->[8,6]
要求:实现时间复杂度O(N*logN)的解法


铺垫:最长递增子序列求解
例如一个数列:2,1,6,4,5,2,7,4
则其最长递增子序列有4个:1,4,5,7或2,4,5,7等
O(n²)解法:
生成一个辅助数组h[8],这辅助数组的用于存放必须以第i个数字结尾的子序列长度,即求出以任何一个数字结尾的数列的最长递增子序列长度。每个数字都要求解以该数字结尾的最长递增子序列长度,即首先是2,以2结尾的最长递增子序列就是2,则h[0]=1;之后是1,以1结尾的最长递增子序列是1,则h[1]=1;之后是6,以6结尾的最长递增子序列是1,6或者2,6,则h[2]=2,以此类推,则例中的h[8]=[1,1,2,2,3,2,4,3]
O(nlogN)解法:
(ps:看到logN一般就会想到是二分法)
此算法是吧之前的枚举过程加速
生成一个辅助数组h[8],首先对于2,直接将2放入,h[0]=2,此时认为之后的h数组都是无效区,只有h[0]是有效区;遇到1时,在有效区中找第一个大于1的数字,利用二分法,因为有效区的有序的,此时由于2>1,则将2换成1,此时h[0]=1,有效区仍然是h[0];
遇到6,在有效区中找第一个大于6的数,但是由于在有效区中没有找到比6大的数,故此时扩充有效区,此时有效区为[1,6],则以6结尾的有效区中的长度为2;
之后是4,在有效区中找到第一个大于4的数,故将6换为4,此时有效区为[1,4],则以4结尾的最长递增子序列是1,4,长度为2;
之后是5,在有效区中扩充,此时有效区为[1,4,5],以5结尾的最长递增子序列为1,4,5,长度为3;
之后是2,将4换为2,此时有效区为1,2,5,则以2结尾的最长递增子序列为1,2,长度为2;
之后是7,扩充有效区,此时以7结尾的最长递增子序列为1,2,5,7,长度为4;
之后是4,此时,将5换为4,此时最长递增子序列为1,2,4,7,即以4结尾的最长递增子序列长度为3。
h[i]的含义是假设当前遍历到cur,h[i]有效区代表遍历到cur为止,长度为i+1的最长递增子序列的最小末尾是什么数,h数组是维持了最小末尾。

public static int[] getdp(int[] arr){
	int[] dp = new int[arr.length];  //以每个数字结尾的最长递增子序列的长度
	int[] ends = new int[arr.length];  
	ends[0] = arr[0];  
	int right = 0;  //有效区长度
	int l = 0;   //有效区的左边界
	int r = 0;   //有效区的右边界
	int m = 0;
	for(int i = 1; i<arr.length; i++){
		l = 0;
		r = right;
		while(l<=r){
		//在有效区中找到最后一个比当前数小的位置
			m = (1+r)/2;
			if(arr[i] > ends[m]){
				l = m + 1;
			}else{
				r = m - 1;
			}
		}
		right = Math.max(right, 1);//若没有找到,扩充有效区
		ends[i] = arr[i];
		dp[i] = 1 + i;//以当前数结尾的最长递增子序列长度
	}
	return dp;
}




以下求解原题:
一种解法是时间复杂度为O(n²),先按照a升序排序,如果a相等时,以b升序排序,得到一个序列,之后在此序列中对b查找最长递增子序列。
本题时间复杂度为O(nLogN)排序策略是,先按照a的值升序排序,当a相等时,以b降序排序。
生成一个辅助数组h[],此时h中放入的是b的值。在a相等时,只关注b,更新h,h只维持b出现的最小末尾。

import java.util.Arrays;
import java.util.Comparator;


public class RussianDollEnvelopes{
	public static class Dot{
		public int w;
		public int h;
		
		public Dot(int weight, int high){
			w = weight;
			h = high;
		}
	}
	
	public static class DotComparator implements Comparator<Dot>{
	//定义Dot比较器,若返回-1,则arg0放在前面,若返回1,表示arg1放在前面
		@Override
		public int compare(Dot arg0, Dot arg1){
			if(arg0.w == arg1.w){
				if(arg0.h == arg1.h){
					return 0;
				}else if(arg0.h < arg1.h){
					return 1;
				}else{
					return -1;
				}
			}else if(arg0.w < arg1.w){
				return -1;
			}else{
				return 1;
			}
		}
	}
	
	public static int maxEnvelopes(int[][] es){
		if(es == null || es.length == 0 || es[0] == null || es[0],length != 2){
		return 0;
		}
	}
	Dot[] dots = new Dot[es.length];
	for(int i =0; i<es.length;i++){
		dots[i] = new Dot(es[i][0], es[i][1]);
	}
	Arrays.sort(dots, new DotComparator());//调用排序方法
	int[] ends = new int[es.length];
	ends[0] = dots[0].h;
	int right = 0;
	int l = 0;
	int r = 0;
	int m = 0;
	for(int i = 1; i<dots.length;i++){
		l = 0;
		r = right;
		while(l <= r){
			m = (1+r)/2;
			if(dots[i].h >ends[m]){
				l = m + 1;
			}else{
				r = m - 1;
			}
		}
		right = Math.max(right, l);
		ends[1] = dots[1].h;
	}
	return right+1;
}


public static void main(String[] args){
	//...
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值