锻炼
时间限制:最多40个用例,1.5秒(C/C++),2秒(Java)/内存限制:256 MB(Stack 1 MB)
哲珉常去的健身房进了一种新的健身器材。该器材是以两柱子竖立的形态,每个柱子上有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
思路:数据有两列,如果对一列排序,那么只需要求另一列的LIS即可
因此,可使用Indexed Tree或者DP+二分查找的方式
使用Indexed Tree时,因为N的范围是10亿,如果直接使用N开数组,会超过可声明的范围,因此需要散列化
package tree; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.StringTokenizer; import java.util.TreeSet; /** * 思路:数据有两列,如果对一列排序,那么只需要求另一列的LIS即可 * 因此,可使用Indexed Tree或者DP+二分查找的方式 * 使用Indexed Tree时,因为N的范围是10亿,如果直接使用N开数组,会超过可声明的范围,因此需要散列化 * @author XA-GDD * */ public class Exercise_0628 { static int T,N,M; static int _Max_M = 300000; static int [][] srcArr = new int[_Max_M+1][2]; static int [] idxTree = new int[_Max_M*2]; static int offset; static TreeSet<Integer> ts = new TreeSet<Integer>(); static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\test_case\\sample_input_0628.txt")); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = Integer.parseInt(st.nextToken()); for(int testCase = 1; testCase<=T;testCase++) { init(); st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); for(int i=0;i<M;i++) { st = new StringTokenizer(br.readLine()); srcArr[i][0]=Integer.parseInt(st.nextToken()); srcArr[i][1]=Integer.parseInt(st.nextToken()); ts.add(srcArr[i][1]); } //对左侧的数据排序,左侧相同时,右侧逆序 Arrays.sort(srcArr,0,M,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]; } }); //散列化 int index=0; for (int val : ts) { map.put(val, index++); } //indexed tree初始化 int k=0; while((1<<k)<index) { k++; } offset=1<<k; for(int i=0;i<M;i++) { int val = query(offset,offset+map.get(srcArr[i][1])-1); //0~当前-1处LIS的最大值 update(offset+map.get(srcArr[i][1]),val+1); //当前处的LIS长度=当前-1处的LIS+1 } System.out.println("#"+testCase+" "+idxTree[1]); } } static void update(int index,int val) { idxTree[index]=val; index = index>>1; while(index>0) { idxTree[index] = Math.max(idxTree[index*2], idxTree[index*2+1]); index = index>>1; } } static int query(int start, int end) { int res =0; while(start<=end) { if(start%2==1) { res = Math.max(res, idxTree[start]); } if(end%2==0) { res = Math.max(res, idxTree[end]); } start=(start+1)>>1; end=(end-1)>>1; } return res; } static void init() { for(int i=0;i<srcArr.length;i++) { Arrays.fill(srcArr[i], 0); } Arrays.fill(idxTree, 0); ts.clear(); map.clear(); } }