OpenJudge-【3528】:最小新整数

3528:最小新整数


Description
给定一个十进制正整数n(0 < n < 1000000000),每个数位上数字均不为0。n的位数为m。
现在从m位中删除k位(0 < k < m),求生成的新整数最小为多少?
例如: n = 9128456, k = 2, 则生成的新整数最小为12456

Input
第一行t, 表示有t组数据;
接下来t行,每一行表示一组测试数据,每组测试数据包含两个数字n, k。
Output
t行,每行一个数字,表示从n中删除k位后得到的最小整数。

Sample Input

2
9128456 2
1444 3

Sample Output

12456
1

题目解析
这是一道稍微提高的贪心算法的题目,可以来检验一下学习的结果。
这里的n非常大,一般来说我们用高精度来计算。但是由于这道题并没有诸如加减乘除之类的运算,我们可以在高精度的基础上,不把字符串转换为数字串,而是直接用字符串string(STL的iostream中的字符串类型),这样做还有一个好处,之后再仔细讲解。
输入不多说,我们用for循环来完成多组数据。然后用cin输入string类型的s,注意这里不需要将字符串s反转,也就是说s[0]~s[n]是从高位到低位排列的。我们把输入的删减次数用int类型F储存。用for循环循环F次,每次循环删减一个数。既然是贪心算法,我们就需要得到一个局部最优解——也就是每次循环(删减一个数)所得到的新字符串该次删减中能够得到的字符串中最小的一个。全局最优解与局部最优解的关系比较像递推,删减是依据上一次的删减结果来判断的。
如何得到局部最优解呢?这就需要寻找一些规律:

一个数,若改变它任意一位的数字(最高位不为0),我们很容易发现 —— 改变的位数越高,与原数的差距就越大。

于是,我们可以判断,要得到局部最优解,就要使新数字的越高的位置数越小。这样就可以知道我们应该从高位往低位判断删减,而要让它小,则应该删除较大的数。这里有一个误区——并不是删除原数中较大的数字就是全局最优解,举个例子:

10009 1

若删除较大的数字则应该删除9,得到1000,但是正确的答案是9——删除1。这个答案的解法有2种,这两种解法描述不同,但是都是指一个东西:

1.若整个字符串为完全非下降序列(前一个数字比后一个数字小或相等,例如123345),则删除字符串的最后一个数字否则删除第一个下降串的第一个数字(如123421,第一个下降串是421,则删除4)。
2.每次删减掉第一个不下降串的最后一个数字

仔细理解能够发现他们是指的一个数字。比如1452325,解法1的是第一个下降串“52”,则删除的是原串的第3个数字“5”;解法2的是第一个非下降串“145”中的“5”,同样是第3个数字!
接下来就是实现代码(这里作者选用解法2)。用for循环遍历当前字符串,当s[i]>s[i+1](第一个非下降结束)或者i==s.length()-1(字符串的末尾)时,删除第i个元素。这里有一个作者用string的原因——string的函数特别多,这里就有一个函数专门删减原串中的一段元素的函数“erase”,s.erase(x,len)表示删除字符串s中从x开始的len个元素,这里用s.erase(i,1),也就是删除i元素本身。由于每次只删除一个数,删除过后就用break跳出循环
最后输出,注意删除前导0!


题外话
今天是作者第一天学习贪心,DP暂时搁在一边。如果程序有一些问题,可以在评论中点出,谢谢!


程序样例

/*Lucky_Glass*/
#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int H;
    scanf("%d",&H);
    for(int h=0;h<H;h++)
    {
        string s;
        cin>>s;
        int F;
        scanf("%d",&F);
        for(int f=0;f<F;f++)
        {
            for(int i=0;i<(int)s.length();i++)
                if(s[i]>s[i+1] || i==s.length()-1)
                {
                    s.erase(i,1);
                    break;
                }
        }
        while(s[0]=='0' && s[1])
            s.erase(0,1);
        cout<<s<<endl;
    }
    return 0;
}

The End

Thanks for reading!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值