洛谷P1106 删数问题——按步骤的贪心法

贪心法可以解决的问题,就是两大类,一种是对元素的贪心,另外一种就是我们这里要讨论的步骤贪心。

洛谷P1106 删数问题

删数本身是一个逆过程,不太符合人正常的认知。不妨考虑选数,即选出l-k个数字构成一个最小的整数。

结合小学数学的知识,高位数可以以一当十,所以只要较高位足够小,不管下面的数据如何挣扎变化 ,对于影响这个数的大小来说无济于事,换句话说就是高位决定整数的大小。
用更像中学数学的话来说,这个问题的求解想法,就是从高到低地选取每一位的时候,优先选择最小的数。每一步选择都是最小的,那么选到最后也必定是一个最小的数。

每一步都必须选最小。这就是我们所说的对步骤的贪心法。这个题目也是非常典型了。

贪心的证明是显然的,只是在证明过程当中,注意说明选第一个最小值,从而不会更坏。这样可以为后面选出更好的结果作铺垫。

注意几个边界情况:即

  1. 前导0
  2. 全是0
#include <bits/stdc++.h>
using namespace std;
int s[260], ans[260], k;
int main()
{
//清洗字符串,转化成int数组,同时得到数组大小
    string tmp;
    cin >> tmp;
    int l = tmp.size();
    for (int i = 0; i < l; i++)
        if (!isdigit(tmp[i])) tmp.erase(i--, 1), l--;
    for (int i = 0; i < l; i++)
        s[i] = tmp[i]-'0';

//贪心
    cin >> k; k = l - k;//确定选数的位数
    fill_n(ans, 260, 10);//初始化,任意个位数必然比10小(废话嘛
    int ti = -1;//目前扩展到的位置,ti及此前的元素已经不可选(给先前的贪心买单,尽管利益仍然是最大化)
    for (int i = 0; i < k; i++)
        for (int j = ti+1; j <= l-k+i; j++)//这个循环保证了选完这个之后,后面必然还能凑出完整的k位数
            if (s[j] < ans[i])//只需要当前位最小
                ans[i] = s[j], ti = j;

//输出
    int i = 0;
    while (ans[i] == 0) i++;//前导零
    if (i == k) cout << 0;//防止全是0
    while (i < k) cout << ans[i++];//正常模式
}

猜你喜欢

转载自blog.csdn.net/weixin_45502929/article/details/106733101