当前位置:首页 > 洛谷题解 > 洛谷P1323题:从集合生成到数字删除解决删数问题

洛谷P1323题:从集合生成到数字删除解决删数问题

2天前洛谷题解58

截图未命名.jpg 洛谷P1323题:从集合生成到数字删除解决删数问题 优先队列 单调栈 贪心算法 洛谷题解 C++ 第1张


这道题目实际上包含两个主要部分:1) 按照特定规则生成集合中的前k个最小元素;2) 将这些数字拼接后删除m个数字,使剩余数字最大。我们需要分别解决这两个子问题。

一、集合元素的生成

集合生成规则告诉我们:

  • 1是集合中的元素

  • 如果P在集合中,那么2×P+1和4×P+5也在集合中

为了高效生成前k个最小元素,我们使用了优先队列(最小)数据结构。这种方法确保了每次都能取出当前最小的元素进行处理。

实现细节

  1. 初始化优先队列,放入起始元素1

  2. 循环直到收集到k个元素:

    • 取出堆顶最小元素

    • 检查是否重复(避免同一元素多次处理)

    • 将该元素加入结果集

    • 生成两个新元素(2×P+1和4×P+5)放入优先队列

这种方法的时间复杂度是O(k log k),因为每个元素最多被处理一次,每次堆操作是O(log k)。

二、数字删除策略

将生成的k个数字拼接成一个大数后,我们需要删除m个数字使剩余数字最大。这类似于经典的"删除k位数字使剩余数字最大"问题。

我们采用了单调栈的方法来解决这个问题:

算法思路

  1. 初始化一个空字符串作为结果

  2. 遍历原数字字符串的每个数字:

    • 当还能删除数字(m>0),且当前数字大于结果字符串的最后一个数字时,删除最后一个数字(m减少)

    • 将当前数字加入结果字符串

  3. 如果遍历完后还有剩余的删除次数(m>0),从结果字符串末尾删除相应数量的数字

这种方法确保了我们总是删除那些"阻碍"数字变大的数字,时间复杂度是O(n),其中n是数字字符串的长度。

三、完整代码

#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

int main() {
    int k, m;
    cin >> k >> m;
    
    // 使用优先队列(最小堆)来生成集合中最小的k个元素
    priority_queue<int, vector<int>, greater<int>> pq;
    vector<int> elements;
    
    pq.push(1);  // 初始元素1
    
    // 生成前k个元素
    while(elements.size() < k) {
        int current = pq.top();
        pq.pop();
        
        // 避免重复元素
        if(!elements.empty() && current == elements.back()) {
            continue;
        }
        
        elements.push_back(current);
        
        // 生成两个新元素加入优先队列
        pq.push(2 * current + 1);
        pq.push(4 * current + 5);
    }
    
    // 将生成的数字拼接成字符串
    string numStr;
    for(int num : elements) {
        numStr += to_string(num);
    }
    
    // 输出删除前的数字
    cout << numStr << endl;
    
    // 删除m个数字使剩余数字最大(单调方法)
    string result;
    int remove = m;
    
    for(char digit : numStr) {
        // 当还能删除数字,且当前数字比结果末尾数字大时,删除末尾数字
        while(remove > 0 && !result.empty() && digit > result.back()) {
            result.pop_back();
            remove--;
        }
        result.push_back(digit);
    }
    
    // 如果还有需要删除的数字,从末尾删除
    if(remove > 0) {
        result = result.substr(0, result.size() - remove);
    }
    
    // 输出删除后的数字
    cout << result << endl;
    
    return 0;
}


四、示例

假设输入k=5, m=2:

  1. 生成前5个元素:1, 3, 5, 7, 9

  2. 拼接成数字字符串:"13579"

  3. 删除2个数字使剩余数字最大:

    • 删除'1'和'3',得到"579"

五、常见问题

  1. 元素重复问题:注意生成的元素可能有重复值,需要跳过处理

  2. 边界条件:当k=1或m=0时的特殊情况

  3. 大数处理:拼接后的数字可能非常大,必须用字符串处理而非数值类型


原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

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