首页 > 试题广场 >

保留最大的数

[编程题]保留最大的数
  • 热度指数:57072 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给定一个十进制的正整数number,选择从里面去掉一部分数字,希望保留下来的数字组成的正整数最大。

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


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

输入

325 
1

输出

35
示例2

输入

3253
1

输出

353
示例3

输入

3253
2

输出

53
 nums = list(input())
cnt = int(input())
n, j, i= len(nums), cnt, 0
while j > 0 and i < n-1:
    if nums[i]>= nums[i+1]:
        i += 1
    else:
        nums.pop(i)
        j -= 1
        n -= 1
        i = i-1 if i > 0 else 0
if j > 0:
    nums = nums[:-j]
print(''.join(nums)) 
复杂度O(n),从左至右,删除小于下一位的数,最后不够删除结尾数
编辑于 2017-09-22 17:24:30 回复(14)
//AC代码:
#include<string>
#include<iostream>
using namespace std;
int main(){
    string s;
    int len,i;
	while(cin>>s>>len){
        i=1;
		while(len--){
            int slen=s.length();
			for(i--;i<slen-1;i++)
				if(s[i]<s[i+1]){
					s.erase(s.begin()+i);
					break;
				}
			if(i==slen-1) s.erase(s.end()-1);
		}
		cout<<s<<endl;
	}
}//总算过了这道题,至今也没想明白为什么用c版的char *字符串就过不去
 //换成c++的string就过去了.......  有毒   有毒

发表于 2017-08-18 10:22:02 回复(22)
/**
思路:从左到右找第一次出现比后面小的数,找到后s就记录下这个数的位置,然后删除
这个位置数字,例如87784201(s记录第二个7位置),如没找到,s值就是记录最后一个
字符,这时其实是三种情况,全部相等或者从左到右递减,或者中间也可能有相等情况,
反正这时s位置记录的数字是最右边最小的 例如:
987654321(s记录1的位置)       
77777777777(s记录最后一个7的位置)   
98877777666555(s记录最后一个5的位置)
每次只删除一个数字,然后重头开始直到删除个数等于cnt,用while的优点是每次不用
遍历完整个数列,遍历个数取决于从高位到低位每次满足删除条件的位置,删除一个后
就进入下一次循环
case明显有错,这代码只能跑50%,目前觉得是最优解法
 */

import java.util.*;
public class Main { 
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            StringBuilder yy = new StringBuilder();
            yy.append(sc.next());
            int cnt = sc.nextInt();
            int count = 0;
            while (count < cnt) {
                int len = yy.length() -1;
                int s = 0;
                while (s < len && yy.codePointAt(s) >= yy.codePointAt(s+1)) 
                s++;
                yy.deleteCharAt(s);
                count++; //记录删除个数   
            }
            System.out.println(yy);
        }
    }
} 

编辑于 2018-05-08 05:19:51 回复(29)

绝逼case有问题
大体思路就是每次删除的元素都是从左往右开始第一个出现的小于其右侧相邻元素的元素。
详细思路:

从左到右遍历字符串,判断其与栈顶字符的大小:若大于栈顶字符,则出栈并继续比较;若不大于栈顶字符或者栈空时,将该字符入栈。每次出栈k减1。

当k为0或者遍历完字符后结束。

若遍历结束后k仍然大于0,则从栈中弹出k个元素。

最后从栈底至栈顶输出元素。若字符串未遍历完,则将剩下的字符输出。

时间复杂度和空间复杂度均为O(n)。

最后通过率为50%

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            char[] str = scanner.next().toCharArray();
            int k = scanner.nextInt();
            process(str, k);
        }
    }

    private static void process(char[] str, int k) {
        int i = 1;
        Stack<Character> stack = new Stack<>();
        stack.push(str[0]);
        while (k > 0 && i < str.length) {
            while (!stack.isEmpty() && k > 0 && stack.peek() < str[i]) {
                stack.pop();
                --k;
            }
            stack.push(str[i]);
            ++i;
        }
        while (k-- > 0) {
            stack.pop();
        }
        printStack(stack);
        printCharArr(str, i);
        System.out.println();
    }

    private static void printStack(Stack<Character> stack) {
        for (char c : stack) {
            System.out.print(c);
        }
    }

    private static void printCharArr(char[] str, int start) {
        while (start < str.length) {
            System.out.print(str[start++]);
        }
    }
}
编辑于 2017-09-14 16:39:22 回复(12)
/*
自己的看法,希望大家批评指正 
其实我觉得测试用例没有问题
题目明确了输入数组的长度在50000量级
因此选择使用string来表示
我做这道题的思路: 题目说删除给定数字的规定个数使之最大
我们就假设这个数 n 位,要删除 b 个元素,使剩下的 n - b个元素组成最大
另外一个数值的大小取决于它的首位 
因为不能改变每个数字的位置,所以其实我们只能在前 b 个元素里选出最大的
作为首位(你只能最多删除 b 个元素,即使 b+1个元素比之前都大
你也没办法把它变成首位)
基于这种思想,b 的大小就是你遍历首位元素(最大元素)的半径
找到前 b 个元素的最大值 max,把 max作为首位,并删除 max之前的元素
这样一来,我们就找到了结果的第一位元素 
接下来只要把 b 的值更新(b = b - 之前删掉的元素个数)
也要把这个数更新(删去它最大值后的子序列(不包含最大值)) 
这样我们就回到了与上面类似的情况,同样的用此时的半径 b (比刚才小)
去找出最大元作为此时的首位(其实是结果的第二位)再更新 b 值和数字序列 
直到最后满足条件退出
下面是我的 AC代码 
*/
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
string fun(string a,int b)
{
    string res="";
    int len = a.length()-b;        //删除 b个,其实就是要填满剩下的 n-b个
                                //我们用上面的方法一个一个填进去    
    while(len!=0)    //len表示要填的长度,填满了退出 
    {
        char max = 0;
        // 找到前 b个中的最大值 
        for(int i=0;i<=b;i++)
        {
            if(max<a[i])
            max = a[i];
        }
        int index = a.find_first_of(max);
        
        b = b - index;    //更新 b 值 
        res = res + max;    //string类型的res存储结果 
        len--;    //填一个格子少一个 
        // 将首位元素之前的删除,也就是更新原来的数字序列 
        a = a.substr(index+1);
        //这是当半径为 0 表示没有再搜索下去的能力了,此时就把后面的元素直接加上 
        if(b==0)
        {
            res = res+a;
            break;
        }
    }
    return res;
}
int main()
{
    string a;
    int b;
    while(cin >> a >> b)
    {
        cout << fun(a,b) << endl;
    }
    return 0;
}

编辑于 2018-04-15 20:56:40 回复(10)
def getMax(n,k):
    res = []
    while k>0:
        if len(n)==k:
            n = []
            break
        maxN = max(n[:k+1])
        res.append(maxN)
        maxin = n.index(maxN)
        n = n[maxin+1:]
        k = k-maxin
    res.extend(n)
    return res

num = input()
k = int(input())
n = ' '.join(str(num)).split()
n = list(map(int,n))     
print(''.join([str(x) for x in getMax(n,k)]))
要删除的个数为k,找出前k+1个中最大者,必定不会被删除,然后删除最大者前面的所有数t个,继续对剩下的数中删除k-t个数,以此类推,直到没有要删除的
编辑于 2018-04-09 21:31:05 回复(1)

题目case虽然有bug,自己想通如何去做就行了;
删除t个数之后的最大值,简单理解就是每次删除1个数都要是最大值;
每次删除数,要使他成为最大,肯定是从高位到低位依次进行比较,那么就可以从高位到低位进行遍历找到降序的那个起点数然后删除就行了,遍历的终点为最后一个数

编辑于 2017-09-12 16:56:18 回复(1)
-----------百思不得姐--------
今天看了通过的各位大佬代码,代码比我简洁多了但时间复杂度其实一样。但你们运行时没报string subscript out of range吗?我给了个if (j < 0) j =0; 通过率就50%了。真的迷!!!

有没有什么特殊的情况,感觉代码没问题的。



编辑于 2017-08-15 08:58:12 回复(11)
/** JAVA 100%CASE通过 代码
 * 思路如下:
 *      从高位开始,每一位的数肯定最大最好。
 *  所以从头查找最大的数。把这个数作为高位,那么这个数就是最大的。
 *  比如:
 *    32918654321 去掉4个数
 *    step1:
 *            从9开始递减,查看第一位最大的数可能是多少
 *            我们查询到第一次出现9的位子下标为2.
 *            这个9前面有2个数比9小,那么如果把9作为第一位的话,那么结果肯定是最大的。
 *          (exception:如果没找到9,则找8,依次递减)
 *    step2:
 *            所以,继续判断,如果9前面的个数是否小于等于要删除的个数。
 *            2<4,说明我们有这么多个数可以删除。
 *          (exception:如果前面的个数大于要去掉的个数,那么这个数就不能作为高位了,就只能退而求其次选择比这个数小的作为高位)
 *    step3:
 *            删除9前面的数 得到数 918654321 由于第一位9已经确定了。
 *            那么我们的需求就变成: 
 *            18654321 去掉2个数
 *    step4:
 *            如果剩下的数的个数正好等于要去掉的个数,那么前面的高位组合就是答案了。
 *            如果剩下的数的个数大于要去掉的个数
 *                且  如果  要去掉的个数为0,那么结束循环,把前面的所有高位和剩下的数组合就是答案了。
 *                    反之,要去掉的个数大于0,则把这个再次带入step1进行循环,
 */
public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    String num = in.nextLine();
    int n = Integer.parseInt(in.nextLine());
    int count = 0;
    while(count<n){
        for(int i=9;i>=0;i--){
            int p = num.indexOf(i+"");
            if(p!=-1&&p<=n-count){
                System.out.print(i);
                count+=p;
                num = num.substring(p+1);
                if(num.length()==n-count){
                    return;
                }
                break;
            }
        }
    }
    System.out.println(num);
}
编辑于 2018-03-22 13:04:57 回复(1)
我想知道输入case:898 2,好多通过了的答案本地调试也是错的,这种“低高低”的case。
思路:
  1. 找到整个字符串中第一个最大的数;
  2. 找到从第一个数到最大数之间的最小数,删除之;
  3. 如果没有最小数,说明第一个最大数左边已经没有可以删除的数,递归处理这个第一最大数右边的字符串;
  4. 直到达到指定次数;
  5. 要排除都是豹子数(类似999)的数。

以下是个人的答案,只不过会超时,求错,求优化,求斧正。

#include <iostream>
#include <algorithm>

static int num = 0;

void function(std::string& s, std::string::iterator b, std::string::iterator e)
{
        if (0 == num) {
                return ;
        }

        if (b + 1 == e || b == e) {
                s.erase(b);
                --num;
                while (num--) {        // 对于999 2这种情况
                         s.erase(s.begin());
                 }
                 return ;
         }

        std::string::iterator max_itr = std::max_element(b, e);
         if (e != max_itr) {
                 std::string::iterator min_itr = std::min_element(b, max_itr);
                 if (min_itr != max_itr) {
                         s.erase(min_itr);
                         --num;
                         function(s, s.begin(), max_itr + 1);
                 } else {
                         function(s, max_itr + 1, s.end());
                 }
         }
         return ;
}

int main()
{
        std::string s;
         while (std::cin >> s >> num) {
                 function(s, s.begin(), s.end());
                 std::cout << s << std::endl;
         }
         return 0;
}


编辑于 2017-08-24 12:24:13 回复(0)
大佬帮看,两种方法都只能20%
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
//	static ArrayList<Character> list = new ArrayList<Character>();
	static LinkedList<Character> list = new LinkedList<>();
	public static void main(String[] args){
		Scanner s = new Scanner(System.in);
		while(s.hasNext()){
			String ss = s.nextLine();
			int delNum = Integer.parseInt(s.nextLine());
			char[] ch = ss.toCharArray();
			for(int i = 0; i<ch.length; i++){
				list.add(ch[i]);
			}
			
			//第一种方法
			int count = 0;
			boolean b = true;
			for(int i=0; i < 10; i++){
				while(b){
					b = list.remove(Character.valueOf(String.valueOf(i).charAt(0)));
					if(b==true){
						count++;
					}
					if(count == delNum){
						break;
					}
				}
				if(count == delNum){
					break;
				}
				b=true;
			}
			//=========================
//          //第二种方法			
//			Arrays.sort(ch);
//			for(int i = 0; i<delNum; i++){
//				list.remove(Character.valueOf(ch[i]));
//			}
			//===============================
			
			StringBuilder sBuilder = new StringBuilder();
			for(int i=0; i < list.size(); i++){
				sBuilder.append(list.get(i));
			}
			System.out.println(sBuilder.toString());
			list.clear();
		}
	}

}

发表于 2017-08-18 21:16:53 回复(7)
莫名其妙qaq,我也不知道,先用了一个错误的方法“莫名其妙”的通过20%
然后感觉用的是对的,但是case只通过40%
之后改了下,莫名地通过,之后又通不过,
用自己的编译器计算
int i = 2;cout<<--i<<endl<<i--<<endl;


然后居然输出
0
2
感觉自己电脑中病毒了,望求大神讲解如何将电脑像手机格式化一样的清除数据了
我什么都不要了!!什么都不要了,c,d,e,f,全部清空!!!!!(好吧c盘本来都有数据,就留着吧)
有办法么????
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#define N 100001
#define M 1001
using namespace std;
int main(){
    string s;
    int len,i;
    while(cin>>s>>len){
        i=1;
        while(len--){
            int slen=s.length();
            for(i--;i<slen-1;i++)
                if(s[i]<s[i+1]){
                    s.erase(s.begin()+i);
                    break;
                }
            if(i==slen-1) s.erase(s.end()-1);
        }
        cout<<s<<endl;
    }
}
/*int main()
{
	int n;
	string a;
	while(cin>>a>>n)
	{
		int i = 1;
		while(n--)
		{
			for(--i;i<a.length()-1;i++)
			{
				if(a[i+1]>a[i])
				{
					a.erase(i,1);
					break;
				}
				if(i==a.length()-2)
				a.erase(a.length()-1);
			}
		}
		cout<<a<<endl;
	}
	return 0;
}*/

/*int main()
    {
    int n;string a;
    while(cin>>a>>n)
        { int i = 1;cout<<a.end();
        while(n--)
            {
           int temp = 0;
            for(--i;i<a.size()-1;i++)
                {
                if(a[i]<a[i+1])
                    {
                    a.erase(i);temp = 1;
                    break;
                }
            }
            if(temp==0) {a.erase(a.end()-1);}
        }
        cout<<a<<endl;
    }
}*/
/*int main(){
    string a;
    while(cin>>a)
    {
    	int n;
    	cin>>n;
    	for(int k = 0;k<n;k++)
    	{
    		int i = a.find("0");
			if(i==a.npos) i = a.find("1");
			if(i==a.npos)	i = a.find("2");
			if(i==a.npos)	i = a.find("3");
			if(i == a.npos)	i = a.find("4");
			if(i ==a.npos)	i = a.find("5");
			if(i == a.npos)	i = a.find("6");
			if(i == a.npos)	i = a.find("7");
			if(i == a.npos)	i = a.find("8");
			if(i == a.npos)	i = a.find("9");
            if(i==a.npos) a.erase(a.end()-1);
			a.erase(i,1);
		}
		cout<<a<<endl;
	}
    return 0;
}*/

/*int i = a.find("q");
    	if(i!=a.npos)
    	cout<<i<<endl;
    	a.erase(i,1);
    	cout<<a<<endl;*/

发表于 2017-08-18 20:43:42 回复(0)
通过30%的case。。。一度怀疑case有毒。
思路是找到0-cnt范围内的最大一个数,再删除最大数之前的所有数,cnt-n,
即把最大的值提到最前一个位置,再将
余下的数列重复同样的操作,直到cnt=0
private static String getMaxAfterCut(StringBuffer sb, int cnt) {
		// TODO Auto-generated method stub
		
		StringBuffer sBuffer = sb;
		if(sBuffer.length()>1000 || cnt>sBuffer.length() || cnt <1 || sBuffer.length()<1) return "";
		int max = Integer.MIN_VALUE;
		int maxpos_start = -1;
		int maxpos_end = maxpos_start;
		
		while(cnt>0) {
			max = Integer.MIN_VALUE;
			int start = maxpos_end+1;
			if(start+cnt >= sBuffer.length()) break;
			for(int i = start;i<start+cnt+1;i++) {
				if(sBuffer.charAt(i) > '9' || sBuffer.charAt(i)<'0') return "";
				if(sBuffer.charAt(i) > max) {
					maxpos_end = maxpos_start = i;
					max = sBuffer.charAt(i);
				}else if(sBuffer.charAt(i) == max) {
					maxpos_end++;
				}
			}
			sBuffer.delete(start, maxpos_start);
			cnt -= (maxpos_start-start);
			maxpos_end -= (maxpos_start-start);
			
		}
		for(;cnt>0;cnt--) { //如果检测到数列结尾cnt不为0,那么再对结尾处的几个数进行删除。
			if(maxpos_end+1 >= sBuffer.length()) {
				sBuffer.deleteCharAt(maxpos_end--);
				continue;
			}
			if(sBuffer.charAt(maxpos_end) < sBuffer.charAt(maxpos_end+1)) {
				sBuffer.delete(maxpos_end-cnt+1, maxpos_end+1);
				break;
			}else {
				sBuffer.deleteCharAt(maxpos_end+1);
			}
		}
		return sBuffer.toString();
	}

编辑于 2017-08-11 14:20:57 回复(7)
  • //我的思路是:从左到右扫描,遇到第一个零就删除,同时删除的计数器减一,检查计数器是否为零,
//不为零,继续往后扫描直到末尾,在从头扫描,遇到第一个一删除,和删除零的做法一样。 //这个想法哪里有问题啊? import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String s = sc.nextLine(); int cnt = sc.nextInt(); StringBuffer str = new StringBuffer(s); for(int i=0;i<10;i++) { for(int j=0;j<str.length();j++) { if(str.charAt(j)-48 == i) { str.deleteCharAt(j); j -= 1; cnt--; } if(cnt==0) { System.out.println(str); return; } } } } }
发表于 2017-09-18 16:42:41 回复(1)
#include <bits/stdc++.h>
using namespace std;

int main(){
    string s;
    int cnt, l, i=0;
    cin>>s>>cnt;
    l = s.length();
    while(i<l-1 && cnt>0){
        if(s[i] >= s[i+1])
            i++;
        else{
            s = s.substr(0, i) + s.substr(i+1);
            l--;
            cnt--;
            i = max(i-1, 0);
        }
    }
    if(cnt)
        s = s.substr(0, l-cnt);
    cout<<s<<endl;

    return 0;
}

发表于 2020-11-24 00:37:58 回复(0)
c++, O(n), monotonous stack
#include <iostream>
#include <algorithm>
#include <string>
#include <stack>
 
using namespace std;
 
string n;
int k;
 
int main(){
    cin >> n >> k;
    stack<char> cur, nums;
    int len = n.length();
    for(int i = len - 1; i >= 0; i --)
        nums.push(n[i]);
    while(!nums.empty() && cur.size() + nums.size() > len - k){
        if(!cur.empty() && nums.top() > cur.top())
            cur.pop();
        else{
            cur.push(nums.top());
            nums.pop();
        }
    }
    while(!nums.empty()){
        cur.push(nums.top());
        nums.pop();
    }
    while(cur.size() > len - k)
        cur.pop();
    string rnt;
    while(!cur.empty()){
        rnt += cur.top();
        cur.pop();
    }
    reverse(rnt.begin(), rnt.end());
    cout<<rnt<<endl;
    return 0;
}


发表于 2020-07-14 20:28:38 回复(0)
import java.util.Scanner;
 /*
   * 123 ,1 升序,就去掉123->23 。
   * 321 ,1 没有升序,从后面去掉321->32.
  */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            StringBuilder sb = new StringBuilder();
            sb.append(sc.next());
            int len = sb.toString().length();
            int cnt = sc.nextInt();
            //异常处理
            if (cnt >= len) {
                System.out.println("0");
            }
            //正常逻辑部分
            int j = 1; //j用来存储之前遍历过的位置
            boolean find = true; //标识这次遍历是否找到过升序的位置
            while (cnt > 0 && find) {
                find = false;
                for (int i = j; i < sb.length(); i++) {
                    int numa = sb.charAt(i - 1) - '0';
                    int numb = sb.charAt(i) - '0';
                    if (numa < numb) {
                        find = true;
                        sb.deleteCharAt(i - 1);
                        cnt--;
                        if (i - 1 > 0) {
                            j = i - 1; //记录上次遍历的位置
                        } else {
                            j = 1;
                        }
                        break;
                    }
                }
            }

            String res = sb.toString();
            if (cnt > 0) {//如果升序对不满足需要,就从后面减去cnt个数
                res = (String) sb.subSequence(0, sb.length() - cnt);
            }
            System.out.println(res);
        }
    }
}

发表于 2019-10-20 00:33:01 回复(0)
AC 代码,90毫秒,python3.5.2
规律:移除一个数字,能得到最大的n-1位数,则优先考虑处于高位的数字,且其紧挨的低位数字比它大,即从左到右第一个处于升序的数字;若没有,说明序列是非升序的,移除最后一个即可。
参考代码
s,n=list(input()),int(input())
assert n>=0 and n<= len(s)
c,i,m=0,0,len(s)-n
while i<len(s)-1 and c < n:
    if s[i] < s[i+1]:
        s.pop(i)
        c+=1
        i = i-1 if i else 0
    else: i+= 1
print(''.join(s[:m]))
编辑于 2018-11-17 17:03:56 回复(0)
为什么啊 一脸懵逼啊

发表于 2018-11-07 11:05:41 回复(1)
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    string str;
    int count;

    while(cin >> str >> count) {
        for(int i = 0;i < count;i++) {
            int haserased = 0;
            for(int j = 0;j < str.size()-1;j++) {
                if(str[j] < str[j+1]) {
                    str.erase(str.begin()+j);
                    haserased = 1;
                    break;
                }
            }
            if(haserased == 0) {
                str.erase(min_element(str.begin(), str.end()));
            }
        }
        cout << str << endl;
    }

    return 0;
}
发表于 2018-08-14 13:15:47 回复(0)