[vijosP1303]导弹拦截(最长上升子序列转LCS)
描述
某国为了防御敌国的导弹袭击,研发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试验阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
格式
输入格式
输入数据只有一行,该行包含若干个数据,之间用半角逗号隔开,表示导弹依次飞来的高度(导弹最多有 20 枚,其高度为不大于 30000 的正整数)。
输出格式
输出数据只有一行,该行包含两个数据,之间用半角逗号隔开。第一个数据表示这套系统最多能拦截的导弹数;第二个数据表示若要拦截所有导弹至少要再添加多少套这样的系统。
样例1
样例输入1
389,207,155,300,299,170,158,65
样例输出1
6,1
很明显这题首先需要求最长上升序列,这题有比较多的做法,我这里选用LCS(最长公共子序列)做法,但问题是最长公共子序列是求A={a1,a2,a3...an}与B={b1,b2,b3....bm}的最长公共子序列,这里只有一个序列串。
所以首先我们需要copy源串,然后对副本进行降序排序,得到一个序列,然后两个序列进行LCS就可以得出最长下降序列了。
然后回溯找出LCS中求出的串,对源串进行删除,最后递归执行直到源串为0就好了。
Java AC Code:
public class Main { //最长下降序列长度 public static int m = 0; //还需要多少飞弹 public static int need = 0; public static void main( String[] args ) { Scanner sc = new Scanner( System.in ); while( sc.hasNext() ) { m=0; need=0; String str = sc.nextLine(); int[] nums = getSplits( str, "," ); List<Integer> list = new ArrayList<Integer>(); for( int i = 0; i < nums.length; i++ ) { list.add( nums[ i ] ); } LCS( nums,list ); System.out.println( m+","+(need-1) ); } } //进行LCS public static void LCS( int[] nums, List<Integer> list ) { int[] sortNums = new int[ nums.length ]; int[][] b = new int[ nums.length + 1 ][ nums.length + 1 ]; int n = nums.length; for( int i = 0; i < n; i++ ) { sortNums[ i ] = nums[ i ]; } Arrays.sort( sortNums ); int[] descNums = new int[ n ]; for( int i = 0; i < n; i++ ) { descNums[ i ] = sortNums[ n - i - 1 ]; } int[][] lcs = new int[ n + 1 ][ n + 1 ]; for( int i = 1; i <= n; i++ ) { for( int j = 1; j <= n; j++ ) { if( nums[ i - 1 ] == descNums[ j - 1 ] ) { lcs[ i ][ j ] = lcs[ i - 1 ][ j - 1 ] + 1; b[ i ][ j ] = 0; } else { if( lcs[ i - 1 ][ j ] > lcs[ i ][ j - 1 ] ) { lcs[ i ][ j ] = lcs[ i - 1 ][ j ]; b[ i ][ j ] = -1; } else { lcs[ i ][ j ] = lcs[ i ][ j - 1 ]; b[ i ][ j ] = 1; } } } } //递归求need,并且判断获取最长的下降序列。 m = lcs[n][n] > m?lcs[n][n]:m; while(list.size() !=0){ printLCS( b, nums, n, n, list ); int[] newNums = new int[list.size()]; for(int i=0;i<list.size();i++){ newNums[i] = list.get( i); } LCS(newNums,list); need++; } } //输入分片 public static int[] getSplits( String str, String pattern ) { String[] splits = str.split( pattern ); int[] ret = new int[ splits.length ]; for( int i = 0; i < ret.length; i++ ) { ret[ i ] = Integer.parseInt( String.valueOf( splits[ i ] ) ); } return ret; } //回溯输出最长序列。 public static void printLCS( int[][] b, int[] nums, int i, int j,List<Integer> list) { if( i == 0 || j == 0 ) return; else if( b[ i ][ j ] == 0 ) { printLCS( b, nums, i - 1, j - 1,list ); list.remove( new Integer(nums[i-1]) ); } else if( b[ i ][ j ] == -1 ) { printLCS( b, nums, i - 1, j,list ); } else { printLCS( b, nums, i, j - 1,list); } } }
关于LCS算法的具体实现,自行百度啦,主要就是进行矩阵Dp,判断相等于不相等时候的选择大小。