【JAVA】最长递增子序列(LIS)的不同解法(POJ 2533)(DP / IndexTree)


题目地址: http://poj.org/problem?id=2533

方法1:DP(贪心 + 二分)

思路:
原始数组DATA[],用于存放原始数据。
临时数组ARR[],用于存放局部最优解(注意:ARR数组中的元素,并不一定是原始数组的子序列)。
计数器T,用于记录当前为止的最长递增子序列长度(ARR数组中有T个有效数字)。

依次读取原始数据的每个元素E;
对于每个元素,进行以下操作:
若ARR[T - 1] < E,则在ARR的有效数字后面补上E(即:ARR[T] = E; T++;,表示最长递增子序列长度加1);
若ARR[T - 1] >= E,则在ARR数组的前T个元素中从左往右找第一个不小于E的(采用二分),用E替换掉(当ARR[index] == E时,替换与否对QRR数组无影响);

时间复杂度:O(N*logN)
空间复杂度:O(N)

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	static int[] DATA, ANSWER;
	static int N, ANSWER_COUNT = 1;

	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		N = Integer.parseInt(br.readLine());

		DATA = new int[N];
		ANSWER = new int[N];

		st = new StringTokenizer(br.readLine());

		ANSWER[0] = Integer.parseInt(st.nextToken());

		for (int n = 1; n < N; n++) {
			int item = Integer.parseInt(st.nextToken());

			if (item > ANSWER[ANSWER_COUNT - 1]) {
				ANSWER[ANSWER_COUNT++] = item;
				continue;
			}

			int index = Arrays.binarySearch(ANSWER, 0, ANSWER_COUNT, item);
			index = index >= 0 ? index : -index - 1;

			ANSWER[index] = item;
		}

		System.out.println(ANSWER_COUNT);
	}
}

方法2:IndexTree(原始数据的下标和Tree叶节点下标对应)

思路:
原始数组DATA,用于存放原始数据及其下标。
IndexTree数组TREE(最大值Tree),叶节点下标与原始数据下标一一对应,叶节点的值代表与该叶节点对应的原始数据结尾的最长递增子序列的长度。

原始数据DATA与下标捆绑,按值升序排序,值相等时,按下标降序排序;
按顺序读取排序后的DATA的下标idx,COUNT = query(S, S + idx) +1;,即可得到原数组以idx为下标的数字结尾的最长递增子序列长度;
TREE[1]即为所求。

时间复杂度:O(N*logN)
空间复杂度:O(N)

PS:时间与空间复杂度实际如下:
时间复杂度:O(2NlogN)
空间复杂度:O(6*N)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Main {
	static int N;
	static int tree[];
	static int arr[][];

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		N = Integer.parseInt(st.nextToken());
		arr = new int[N][2];

		st = new StringTokenizer(br.readLine());

		for (int i = 0; i < N; i++)
			arr[i] = new int[] { Integer.parseInt(st.nextToken()), i };

		int cnt = 16384;
		tree = new int[2 * cnt];

		// 对数组排序
		Arrays.sort(arr, 0, N, new Comparator<int[]>() {
			@Override
			public int compare(int[] o1, int[] o2) {
				if (o1[0] == o2[0]) {
					return o2[1] - o1[1];
				}
				return o1[0] - o2[0];
			}
		});

		for (int i = 0; i < N; i++) {
			int val = query(cnt, cnt + arr[i][1] - 1) + 1;
			update(cnt + arr[i][1], val);
		}

		System.out.println(tree[1]);
	}

	static int query(int s, int e) {
		int ret = 0;
		while (s <= e) {
			if (s % 2 == 1) {
				ret = Math.max(tree[s], ret);
			}

			if (e % 2 == 0) {
				ret = Math.max(tree[e], ret);
			}

			s = (s + 1) / 2;
			e = (e - 1) / 2;
		}

		return ret;
	}

	static void update(int index, int value) {
		while (index > 0) {
			tree[index] = Math.max(tree[index], value);
			index = index / 2;
		}
	}
}

方法3:IndexTree(原始数据的值和Tree叶节点下标对应)

思路:
原始数组DATA,用于存放原始数据及其下标。
IndexTree数组TREE(最大值Tree),叶节点下标与原始数据的值一一对应(原数据中相同值对应同一个叶节点),叶节点的值代表以该叶节点代表的值结尾的最长递增子序列的长度。

按顺序读取DATA,COUNT = query(S, S + value) +1;,即可得到原数组中以value结尾的最长递增子序列长度;
TREE[1]即为所求。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	static int N;
	static int tree[];

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		N = Integer.parseInt(st.nextToken());
		tree = new int[2 * 16384];

		st = new StringTokenizer(br.readLine());

		for (int i = 0; i < N; i++) {
			int val = Integer.parseInt(st.nextToken());
			update(16384 + val, query(16384, 16384 + val - 1) + 1);
		}

		System.out.println(tree[1]);
	}

	static int query(int s, int e) {
		int ret = 0;
		while (s <= e) {
			if (s % 2 == 1) {
				ret = Math.max(tree[s], ret);
			}

			if (e % 2 == 0) {
				ret = Math.max(tree[e], ret);
			}

			s = (s + 1) / 2;
			e = (e - 1) / 2;
		}

		return ret;
	}

	static void update(int index, int value) {
		while (index > 0) {
			tree[index] = Math.max(tree[index], value);
			index = index / 2;
		}
	}
}

方法4:BIT(原始数据的值和Tree叶节点下标对应)

思路:
与方法二一致。

时间复杂度:O(N*logN)
空间复杂度:O(N)

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	public static int[] bit = new int[32008];
	public static int[] reslut = new int[32008];
	public static int n;
	public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	public static StringTokenizer st = null;

	public static void main(String[] args) throws Exception {
		n = Integer.parseInt(in.readLine());
		for (int i = 1; i <= n; i++) {
			st = new StringTokenizer(in.readLine());
			int x = Integer.parseInt(st.nextToken()) + 1;
			int y = Integer.parseInt(st.nextToken());
			add(x, 1);
			reslut[sum(x) - 1]++;
		}
		for (int i = 0; i < n; i++)
			System.out.println(reslut[i]);
	}

	public static void add(int id, int c) {
		for (int i = id; i < 32008; i += (i & (-i)))
			bit[i] += c;
	}

	public static int sum(int id) {
		int s = 0;
		for (int i = id; i >= 1; i -= (i & (-i)))
			s += bit[i];
		return s;
	}

	public static int sum(int s, int t) {
		return sum(t) - sum(s - 1);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值