洛谷P1106 删数问题

题目描述

​ 输入一个高精度的正整数 n(长度不大于 240 位),去掉其中任意 s 个数字后剩下的数字按原左右次序将组成一个新的正整数,现求一种方案,使得新的正整数数值最小。


输入

​ 第一行一个整数 n。

​ 第二行一个正整数 s。

输出

​ 输出一个数表示最小值,输出时忽略数字的前导零。


样例输入1
179566
4
样例输出1
15
样例输入2
903071
3
样例输出2
1

本题很明显应该采用贪心算法解题,问题在于贪心策略的选择。这道题令人迷惑的点在于,乍一看似乎很容易得出所谓的“贪心策略”:把给的这几个数里最大的那n个删掉,似乎就可以了。冒出这样的念头之后用这个策略看一眼这俩具有误导性的测试用例——完全OK,于是二话不说就开始写。唰唰唰写完提交,一看,一大串令人眼前一黑的红色,WA了。上述全部为本人亲身经历。

回到我们的问题。我们要使得删除部分数字后得到的数最小,那么我们可以去想,去按照贪心的思路去想:若是我们只删除一个数字,要使得这个数变得最小,我们应当删除哪一个?仅仅是选择删除最大的那个吗?怎么浅显易懂的错误,很容易想出反例:8123456789,删除最大的那个变成812345678,但我们若是删除第一个:123456789,这明显比上一个要小。

贪心算法不像动态规划,它只关心局部最优。那么,删除哪一个数字能达到局部最优呢?我们可以发现,只要删除从左往右数第一个逆序对的第一个数,就能达到我们想要的效果。因为若是删除顺序排序的数列中的任何一个数字,得到的新数一定会比原来要大,我这里就不举例证明了,自己试一下很容易就能得到这个结论。至于为什么一定要是第一个逆序对,很简单,因为它的位数最大,举个很明显的例子:124398,很容易就明白了。

上述也算是一个小结论,可以作为灵感或用于日后所遇到的类似的题中。

代码如下:

#include 
#include
#include
using namespace std;

int main()
{
    string s;
    int n;
    cin >> s >> n;
    for (int i = 0;i < n;i++)
    {
        int j = 0;
        while (s[j + 1] && s[j] <= s[j + 1]) ++j;
        s.erase(s.begin() + j);
    }
    while (!s.empty() && s[0] == '0')//去除前导0
        s.erase(s.begin());
    if (s.empty())
        cout << 0;
    else cout << s;

    return 0;
}

你可能感兴趣的:(算法,c++,贪心算法)