【JAVA】有关系数对中两侧均递增的最大对数Pro20210628(最长递增子序列(LIS) / IndexTree / DP + 二分)
题目
哲珉常去的健身房进了一种新的健身器材。该器材是以两柱子竖立的形态,每个柱子上有N+1个把手,各把手都以从下到上的方向贴着0到N的编号。
使用该器材进行锻炼时,需要用左手握住左侧柱子的0号把手,用右手握住右侧柱子的0号把手。然后,将双手向上移动到更高的把手来持续进行锻炼。
该器材附有一张表,表上以成对的形式写着多个左右把手的编号。锻炼者只能同时握住表上写着的一对把手编号。即,若表上有(3, 4),那么可以同时握住左侧3号把手和右侧4号把手。若表中没有(2, 6),那么不能同时握住左侧2号把手和右侧6号把手。
哲珉希望通过一次获得最大程度的锻炼,但锻炼量和他同时移动双手的次数成正比。换句话说,当双手移动的次数越多,能达到的锻炼量就越大。
比如说,假设N=10,并且表中的成对编号为 (2, 3)、(1, 1)、(5, 5)、(7, 4)、(6, 8)、(10, 1)。在这种情况下,双手移动次数最多的一种方法是以 (0, 0)、(1, 1)、(2, 3)、(5, 5)、(6, 8) 的顺序握住把手。这时,双手移动的次数为四次,然而锻炼量达到了最大。
请写一个程序,通过接收健身器材的表来计算双手能移动的最大次数。
[限制条件]
1.把手的最大编号N是介于1到1,000,000,000之间。
2.给出的编号成对个数M是介于1到300,000之间。
[输入]
第一行给出测试用例数量T。接着给出T个测试用例。各个用例的第一行给出把手编号的最大值N和成对个数M。接下来通过M行,每行给出一对左右把手的编号。
[输出]
各测试用例的答案按照顺序标准输出。每个测试用例输出“#x”(其中x表示测试用例编号,从1开始),加一个空格(不带引号),输出题目中要求的双手能移动的最大次数。
[输入输出 示例]
(输入)
2
10 6
2 3
1 1
5 5
7 4
6 8
10 1
14 8
8 10
1 2
3 4
2 3
14 13
4 4
9 13
7 9
(输出)
#1 4
#2 6
思路
左右两侧同时长递,那么就先确保一侧递增,再在另一侧求最长递增子序列即可。
所以,先将数对儿的一侧进行升序排序,再在另一侧求最长递增子序列即可。
方法1:IndexTree
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
public class Pro20210628IndexTree {
static int[] tree;
public static void update(int i, int val) {
while (i > 0) {
tree[i] = Math.max(tree[i], val);
i /= 2;
}
}
public static int query(int s, int e) {
int result = 0;
while (s <= e) {
if (s % 2 > 0)
result = Math.max(result, tree[s]);
if (e % 2 == 0)
result = Math.max(result, tree[e]);
s = (s + 1) / 2;
e = (e - 1) / 2;
}
return result;
}
public static void main(String[] args) throws Exception {
System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210628.txt"));
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
for (int t = 1; t <= T; t++) {
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());
int idx = 1;
while (idx < M)
idx = idx << 1;
int[][] data = new int[M][2];
tree = new int[idx * 2];
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
data[i][0] = Integer.parseInt(st.nextToken());
data[i][1] = Integer.parseInt(st.nextToken());
}
Arrays.sort(data, 0, M, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0];
}
});
for (int i = 0; i < M; i++) {
data[i][0] = data[i][1];
data[i][1] = i;
}
Arrays.sort(data, 0, M, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0];
}
});
for (int i = 0; i < M; i++) {
int length = query(idx, idx + data[i][1] - 1) + 1;
update(idx + data[i][1], length);
}
System.out.println(tree[1]);
}
}
}
方法2:贪心+二分
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
public class Pro20210628DP {
public static void main(String[] args) throws Exception {
System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210628.txt"));
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
for (int t = 1; t <= T; t++) {
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());
int[][] data = new int[M][2];
int[] lis = new int[M];
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
data[i][0] = Integer.parseInt(st.nextToken());
data[i][1] = Integer.parseInt(st.nextToken());
}
Arrays.sort(data, 0, M, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0];
}
});
int length = 1;
lis[0] = data[0][1];
for (int m = 1; m < M; m++) {
int item = data[m][1];
if (item > lis[length - 1]) {
lis[length++] = item;
continue;
}
int index = Arrays.binarySearch(lis, 0, length, item);
index = index >= 0 ? index : -index - 1;
lis[index] = item;
}
System.out.println(length);
}
}
}