【JAVA】最长递增子序列(LIS)的不同解法(POJ 2533)
题目地址: 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);
}
}