当前位置:首页 > 洛谷题解 > 洛谷P1106题解:贪心策略与单调栈实现删除k位最小数

洛谷P1106题解:贪心策略与单调栈实现删除k位最小数

2周前 (08-15)洛谷题解75

截图未命名.jpg 洛谷P1106题解:贪心策略与单调栈实现删除k位最小数 贪心算法 单调栈 字符串 洛谷题解 C++ 第1张

字符串处理贪心算法的结合常常考验我们对问题本质的理解。本文将以洛谷P1106题为例,详细介绍如何通过贪心算法删除k位数字使剩余数字最小,帮助新手掌握单调栈贪心算法的应用技巧。

一、问题解析

题目要求给定一个字符串表示的数字和一个整数k,删除其中k位数字,使得剩余数字形成的数最小。核心挑战在于:

  1. 必须删除恰好k位数字

  2. 数字顺序不能改变

  3. 需要去除结果中的前导零

  4. 处理结果为空的情况

贪心策略核心思想‌:

  • 局部最优导致全局最优‌:尽可能让高位的数字变小

  • 单调处理‌:维护一个单调不降的序列

  • 前导零处理‌:删除结果中开头的所有零

二、完整代码

#include <iostream>
#include <string>
#include <vector>
using namespace std;

// 删除k位数字的函数
string removeKDigits(string num, int k) {
    // 使用vector模拟栈结构
    vector<char> stack;
    
    // 遍历输入数字的每一位
    for (char digit : num) {
        // 贪心策略:当栈非空、还有删除次数且栈顶数字大于当前数字时
        while (!stack.empty() && k > 0 && stack.back() > digit) {
            stack.pop_back(); // 删除栈顶数字(删除高位较大的数字)
            k--; // 减少剩余删除次数
        }
        // 将当前数字加入栈中
        stack.push_back(digit);
    }
    
    // 如果还有k需要删除,从末尾删除(此时栈中已是单调不减序列)
    while (k-- > 0) {
        stack.pop_back(); // 删除末尾较大的数字
    }
    
    // 构建结果字符串并去除前导零
    string result; // 存储最终结果
    bool leadingZero = true; // 标记是否处于前导零状态
    
    // 遍历栈中每个数字
    for (char digit : stack) {
        // 跳过前导零
        if (leadingZero && digit == '0') continue;
        // 遇到非零数字或已有非零数字,结束前导零状态
        leadingZero = false; 
        result += digit; // 将数字添加到结果
    }
    
    // 处理结果为空的情况(如删除所有数字)
    return result.empty() ? "0" : result;
}

int main() {
    string num; // 存储输入的数字字符串
    int k;      // 要删除的数字位数
    cin >> num >> k; // 读取输入
    
    // 调用函数并输出结果
    cout << removeKDigits(num, k) << endl;
    return 0;
}

三、算法解析

1. 贪心删除策略

算法的核心在于维护一个‌单调不降的序列‌:

  • 遍历数字字符串的每个字符

  • 当满足三个条件时弹出栈顶元素:

    1. 栈非空

    2. 还有剩余删除次数(k > 0)

    3. 栈顶数字 > 当前数字

  • 将当前数字压入栈中

为什么这样有效‌:

  • 高位数字的权重更大,优先优化高位

  • 当高位数字大于低位时,删除高位更有利于数值减小

  • 示例:num="14329", k=3

    1. 删除4(1<4>3 → 删除4)

    2. 删除3(1<3>2 → 删除3)

    3. 删除9(最后结果:12)

2. 后续处理

  • 剩余删除处理‌:如果遍历结束后仍有剩余的k,删除末尾k个数字

    • 原因:此时栈中已是单调不降序列,末尾数字最大

  • 前导零处理‌:跳过结果开头的所有零

  • 空结果处理‌:当结果为空时返回"0"

四、常见错误

  1. 未处理前导零‌:可能导致"00234"输出错误

  2. 忽略空结果‌:删除所有数字后应返回"0"

  3. 错误处理剩余k‌:遍历后剩余的k必须从末尾删除

  4. 顺序变更‌:改变了数字的相对位置

  5. 栈使用不当‌:使用vector模拟栈可高效插入删除



原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。