删数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

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

dp[i][i]=a[i];

path[i][i]=i;

}

//填表

for(int r=1;r

for(int i=0;i

int j=i+r;

if(dp[i][j-1]

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+1i,则a1...ai...ak-1ak+1...an,很明显这个数>a1...ai+1...an。如果删除ka1...ai+1...an:

\(\left\{

\begin{aligned}

a1...ai+1...an,{删除a_i}\\

{\left\{

\begin{aligned}a1...ai...ak-1ak+1...an,删除a_k,k>i\\

a1...ak-1ak+1...ai...an,删除a_k,k

\end{aligned}

\right.

\)

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

if(S==0)

break;

if(a[i]>a[i+1])

{

flag[i]=true;

S--;

}

}

for(int j=0;j

if(!flag[j])

System.out.print(a[j]);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值