zoukankan      html  css  js  c++  java
  • 删数问题

    问题描述

    从键盘输入一个高精度的正整数n(<=240位),去掉其中任意S个数字后,剩下的数字按照从左到右的顺序重新组成一个新的数字,寻找一种方案,使得剩余的数字最小

    例如,n=178543,S=4 最优解13

    递归

    假设刚开始的这一个正整数n,表示为(a_1)...(a_n),而最优解表示为(b_1)...(b_l)其中l=n-S。其实我们可以看到,如果(b_i)确定下来了,(b_i)=(a_j),那么(b_{i+1})应该从(a_{j+1})(a_{n-(l-i)})中选择最小的,n-(l-i)是因为必须保证后面存在这么多位数字的情况下这一位最小。
    (b_i)=(min) (a_i,{start<=i<=n-num})num表示后面所剩的位数,本来应该是n-num+1,但是这里是<=,所以是n-num

    Java代码实现

    将n表示为数组的形式

            private static int num=2;//初始值是S,表示这个数后面还有几位
    	public static void getSum(int[]a,int start) {//start表示a中可以开始的位置
    		if(num==0)//这是最后一个数,递归出口
    			return;
    		int min=a[start];
    		int index=start;
    		for(int i=start;i<=a.length-num;i++) {
    			if(min==0)//此处首位也可以是0
    				break;
    			else if(min>a[i]) {
    				min=a[i];
    				index=i;
    			}
    		}
    		if(index==a.length-num)//最后的都不变
    		{
    			for(int i=index;i<a.length;i++) {
    				System.out.print(a[i]);
    			}
    			return;
    		}
    		System.out.print(min);
    		num--;//这一位找到了
    		getSum(a,index+1);//进行从index+1到最后的子问题
    	}
    

    将n表示为String,用ASCII码

        private static int num=2;//初始值是S,表示这个数后面还有几位
        public static void getSum(String str,int start) {
    		if(num==0)//这是最后一个数,递归出口
    			return;
    		int index=start;
    		for(int i=start;i<=str.length()-num;i++) {
    			if(str.charAt(index)==48)//0
    				break;
    			else if(str.charAt(index)>str.charAt(i))
    				index=i;
    		}
    		if(index==str.length()-num) {
    			System.out.println(str.substring(index));
    			return;
    		}
    		System.out.print(str.charAt(index));
    		num--;//这一位找到了
    		getSum(str,index+1);//进行从index+1到最后的子问题
    	}
    

    DP

    其实可以想象到,这里做了许多重复比较。所以也可以用填表法(DP)。a[i][j]表示从i到第j的最小值,那么就是找S次最小值就可以了。

    Java实现代码

    public static void getSum(int[]a,int S) {
    		int[][]dp=new int[a.length][a.length];
    		int[][] path=new int[a.length][a.length];//记录在a数组中的位置,求最优解时用这个下标+1
    		//初始值
    		for(int i=0;i<a.length;i++) {
    			dp[i][i]=a[i];
    			path[i][i]=i;
    		}
    		//填表
    		for(int r=1;r<a.length;r++)
    		for(int i=0;i<a.length-r;i++) {
    			int j=i+r;
    			if(dp[i][j-1]<dp[i+1][j]) {
    				dp[i][j]=dp[i][j-1];
    				path[i][j]=path[i][j-1];//左边
    			}else {
    				dp[i][j]=dp[i+1][j];
    				path[i][j]=path[i+1][j];//右边
    			}
    		}
    		int start=0;
    		for(int i=S;i>0;i--) {
    			System.out.print(dp[start][a.length-i]);
    				start=path[start][a.length-i]+1;
    		}
    	}
    

    可以按照同递归的方法将对数组的操作变为对字符串的,以便应对更多位数的n。
    其实DP进行了许多无用计算,算法仍然可以优化。明天再更。

    贪心策略

    我们先设想一下,如果一堆数字只需要删除一个。那么应该删除从左往右第一个极大值,因为如果数字是升序排列的,只需要删除最后一个(即最大的)。当数字不是升序的,假设a1..an,ai为从左向右第一个极大值(即ai+1<ai),如果删除ai所得为a1...ai+1...an,如果不删除ai删除k>i,则a1...ai...ak-1ak+1...an,很明显这个数>a1...ai+1...an。如果删除k<i则a1...ak-1ak+1...ai...an则这个数也>a1...ai+1...an:
    (left{ egin{aligned} a1...ai+1...an,{删除a_i}\ {left{ egin{aligned}a1...ai...ak-1ak+1...an,删除a_k,k>i\ a1...ak-1ak+1...ai...an,删除a_k,k<iend{aligned} ight.} end{aligned} ight. )
    k!=i时两种都比a1...ai+1...an大,所以删除(a_i)是最优解。

    这是删除一个的时候,当删除S个时,就是重复上述过程S次

    Java实现代码

    public static void getSum2(int[]a,int S) {
    		//从左到右找到第一位极大值
    		boolean[] flag=new boolean[a.length];
    		for(int i=0;i<a.length-1;i++) {
    			if(S==0)
    				break;
    			if(a[i]>a[i+1])
    				{
    				flag[i]=true;
    				S--;
    				}
    		}
    		for(int j=0;j<a.length-S;j++) {
    			if(!flag[j])
    				System.out.print(a[j]);
    		}
    	}
    
  • 相关阅读:
    证书格式转换
    emq知识点
    emq共享订阅
    SpringBoot
    Android网络编程Socket长连接
    Android 网络通信框架Volley简介(Google IO 2013)
    Android中的5种数据存储方式
    Android
    android解析XML总结(SAX、Pull、Dom三种方式)
    乔迁新禧
  • 原文地址:https://www.cnblogs.com/code-fun/p/12790925.html
Copyright © 2011-2022 走看看