例题:
键盘输入一个正整数n,去掉其中任意s个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的n和s,寻找一种方案使得剩下的数字组成新数最小。(n不超过240)
问题分析:
在位数固定的情况下,让高位的尽量小,其值也就较小,依据此贪心策略就可以解决问题。
为了实现贪心策略来删除数字,具体的就是:
相邻的两个数做比较,若高位比低位大则删除高位,若小,就与下一组相比较。
下面看一个实例:
由实例n1,相邻的数字只需要向后比较;而从实例n2开始可以看出,当第i位与第i+1位比较,若删除第i位后,必须向前考虑第i-1位与第i+1位进行比较,才能保证结果的正确性。
由此可知通过实例设计算法时,枚举的实例一定要有全面性。要尽可能的多列举几个不同的实例,在看如下的两个特殊情况:
经过以上的案例分析我们可以进行算法设计:
该算法主要由五部分组成:初始化,相邻的数字比较,处理过程中的删除不足够s位的情况,高位有0的情况和结果输出。
这里我们选择一种最简单也最好理解的方法**“物理进行字符删除”**
就是用后面的字符覆盖已删除的字符,字符串长度改变。但是这样可能会有较多的字符移动操作,算法的效率不高。
在用这种算法的时候,我们需要在继续一次删除后,就退出该循环,继续下次循环。反复运行s次后,判断以上特殊的情况,处理完整后,就可输出结果:
Java版源码:
/**
* 最小正整数问题
*
*/
public class minPositiveInt {
static String str;
static int length; //数字的长度
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int []date = new int [240];
char []array =new char [240];
System.out.println("输入数:");
str = scanner.nextLine();//读数
length = str.length();//str的长度
for(int i=0;i<str.length();i++) {//将数存入char数组中
array[i] = str.charAt(i);
}
System.out.println("请输入需要删除几个数:");
int flag = scanner.nextInt();
int sum = 0;//删除次数
if(flag>length) {
System.out.println("错误!删除的比原数字长!!");
}else {
for(int i=0;i<flag;i++) {
for(int j=0;j<length-1;j++) {
if(array[j]>array[j+1]) {
delete(array,j);//删除array[j]
sum++;
break; //删除了一个后退出
}
}
}
if(sum < flag) {//判断是否删除了规定的数量,没有进入if语句
int a = flag-sum;
length -= a;
}
}
//输出数组
System.out.println("所求最小正整数为: ");
for(int i=0;i<length;i++) {
System.out.print(array[i]);
}
}
//删除方法
private static void delete(char[] array, int j) {
for(int i=j;i<length-1;i++) {
array[i] = array[i+1];
}
length--;
}
}
这是第一种算法,虽然简单好理解但是多次的移位,让代码的效率不是那么高。所以接下来我介绍另外两种方法:
1.可以利用数组记录字符的存在状态,元素值为1表示对应的数字存在,元素值为0表示对应的元素值已删除。这样避免了字符的移动,字符串的长度不变,可以省略专门记录删除数字的位置。但是这样做前后数字的比较过程和最后的输出过程会相对麻烦一点
2.还是使用数组,记录未删除字符的下标,粗略的过程如下:
这时该数组就像一个数据库的索引文件。但是此方法同样存在着操作复杂的问题。
总结:
以上就是这个问题我所罗列的三个方法,我只提供了第一种方法的源码,大家可以直接尝试的把后面两种方法实现出来。如果有什么疑问可以评论我们一起解决。
对于这个问题,因为我也是一个初学者,我目前还没掌握一个操作又简单同时后面的运算也不复杂的算法,希望各位大佬可以提一些建议,或者那块有问题可以指出我加以改进。