【JAVA】有关系数对中两侧均递增的最大对数Pro20210628 (最长递增子序列(LIS) / IndexTree / DP + 二分)

【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);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值