牛客网在线编程题 保留最大的数

题目描述

给定一个十进制的正整数number,选择从里面去掉一部分数字,希望保留下来的数字组成的正整数最大。

输入描述:

输入为两行内容,第一行是正整数number,1 ≤ length(number) ≤ 50000。第二行是希望去掉的数字数量cnt 1 ≤ cnt < length(number)。

输出描述:

输出保留下来的结果。
示例1

输入

325 1

输出

35

这道题在牛客网的通过率偏低,目前6%都不到。主要是大多数人没有get到这题的解题技巧,看了讨论,大多数人说的通过率都不超过50%。这题在算法课上老师讲过可以用贪心法算出来。但是这里这题的数据规模非常大,输入的数据长度在1-50000之间,需要注意的是长度为1-50000,并不是说输入的数为1-50000,估计很多人没看到这个要求。用Java编程的定义整数是真的很习惯性的就定义成int,像这种题往往是有陷阱的。像这么长的数字,用long也存不到,然后并不需要计算,所以可以用String或StringBuilder。

解这道题,可以先举几串数字来看下。

比如45678,这种递增的数字很显然去掉第一位就是最大了吧。65432,而这种递减的也很明显去掉最后一位就可以了。12321,这种的话也是去掉第一位就可以了。32123,这种通过观察也知道是去掉中间那个1。通过这四种数字,可以发现每次去掉的那位都是从左往右的开始呈递增趋势的第一位数字。可以理解成是第一次遇到的“低谷”。

先呈上我能通过80%测试用例的代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(
				new InputStreamReader(System.in));
		String num = br.readLine();
		int cnt = Integer.parseInt(br.readLine());
		char[] nc = num.toCharArray();//将输入的数字转成字符数组
		int len = nc.length;//先定义个变量存储长度

		for (int i = 0; i < cnt; i++) {
			for (int j = 0; j < len - i; j++) {
				if (j == 0) {
					if (nc[j] < nc[j + 1]) {
						dealWith(nc, i, j);
						break;
					}
				} else if (j == len - i - 1) {
					if (nc[j] >= nc[j - 1]) {
						dealWith(nc, i, j);
						break;
					}
				//这里注意不能写成nc[j] <= nc[j - 1] && nc[j] <= nc[j + 1]
				} else if ((nc[j] <= nc[j - 1] && nc[j] < nc[j + 1])
						|| (nc[j] < nc[j - 1] && nc[j] <= nc[j + 1])) {
					dealWith(nc, i, j);
					break;
				}
			}
		}

		for (int i = 0; i < len - cnt; i++) {
			System.out.print(nc[i]);
		}
	}

	/**
	 * 删除第j位数字
	 * @param nc
	 * @param i 将要去掉的第i位
	 * @param j 待删除的索引
	 */
	private static void dealWith(char[] nc, int i, int j) {
		//将从j到nc.length - i - 1位的数字向左移动
		for (int k = j; k < nc.length - i - 1; k++) {
			nc[k] = nc[k + 1];
		}
		//最后那位就置为0就好了,反正后面用不到了
		nc[nc.length - i - 1] = 0;
	}
}

只能通过80%说明还存在一些问题,数字太长的话没法计算,而且有两层for循环,显然不是最好的解题方法。

附上别人的解法,我做了点改进,能全部通过。解题思想其实差不多,这里只不过是一次删除多个数字。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

	/**
	 * 思路如下: 从高位开始,每一位的数肯定最大最好。
	 * 
	 * 所以从头查找最大的数。
	 * 
	 * 把这个数作为高位,那么这个数就是最大的。  
	 * 
	 * 比如: 32918654321 去掉4个数   
	 * 
	 * step1:
	 * 
	 * 从9开始递减,查看第一位最大的数可能是多少 ,我们查询到第一次出现9的位子下标为2.      
	 * 
	 * 这个9前面有2个数比9小,那么如果把9作为第一位的话,那么结果肯定是最大的。
	 * 
	 * (exception:如果没找到9,则找8,依次递减)
	 *
	 * step2:
	 *
	 * 所以,继续判断,如果9前面的个数是否小于等于要删除的个数。
	 *
	 * 2<4,说明我们有这么多个数可以删除。
	 * 
	 * (exception:如果前面的个数大于要去掉的个数,那么这个数就不能作为高位了,就只能退而求其次选择比这个数小的作为高位)    
	 * 
	 * step3:
	 * 
	 * 删除9前面的数 得到数 918654321
	 * 
	 * 由于第一位9已经确定了。那么我们的需求就变成918654321(去掉2个数)
	 *
	 * step4:
	 * 
	 * 如果剩下的数的个数正好等于要去掉的个数,那么前面的高位组合就是答案了。
	 * 
	 * 如果剩下的数的个数大于要去掉的个数
	 * 
	 * 且如果要去掉的个数为0,那么结束循环,把前面的所有高位和剩下的数组合就是答案了。      反之,要去掉的个数大于0,
	 * 则把这个再次带入step1进行循环,
	 * 
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(
				new InputStreamReader(System.in));
		String num = br.readLine();
		StringBuilder sb = new StringBuilder(num);
		int n = Integer.parseInt(br.readLine());
		int count = 0;// 表示删除到的第count个数字

		while (count < n) {
			// 从9开始找数串中最大的数
			for (int i = 9; i >= 0; i--) {
				// 找到最大的数第一次出现的位置
				int p = sb.indexOf(i + "");
				// 能找到,并且能把p索引之前的数字删掉
				if (p != -1 && p <= n - count) {
					// 把i打印出来
					System.out.print(i);
					// 直接跳过p位
					count += p;
					// 把p索引后的字符串截取出来
					num = sb.substring(p + 1);
					sb = new StringBuilder(num);
					// 刚好删除完要删的数字,返回退出了
					if (sb.length() + count == n) {
						return;
					}
					break;
				}
			}
		}

		//把剩下的也打印出来
		System.out.println(sb.toString());
	}
}
还有个问题,在Java中输入格式也会影响程序的运行时间,常用的有两种输入,一种是用Scanner,另一种是用BufferedReader。在输入数据类型为数字的时候,用Scanner会比较方便,因为API已经封装好了转换的方法,而用BufferedReader需要手动转换。但是在运行速度上,BufferedReader会快一些,特别是输入的字符串很长的时候。所以,在控制输入格式的时候我比较常用BufferedReader,当然也要视题目而定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值