信息学奥赛一本通 1321:【例6.3】删数问题(Noip1994) | 洛谷 P1106 删数问题

【题目链接】

ybt 1321:【例6.3】删数问题(Noip1994)
洛谷 P1106 删数问题

【题目考点】

1. 贪心

【解题思路】

解法1:每次找k+1个数中的最小值

假设我们从左向右扫描每位数字,分别判断该位数字是否应该被删掉。此时各位数字为:
d 1 d 2 . . . d n d_1d_2...d_n d1d2...dn,要在其中删掉 k k k位数字。
删掉k位后剩下 n − k n-k nk位数字,最高位数字为 d x d_x dx
如果不删 d 1 d_1 d1,最高位为 d 1 d_1 d1;如果删除 d 1 d_1 d1不删 d 2 d_2 d2,最高位为 d 2 d_2 d2;。。。
d 1 ∼ d k + 1 d_1\sim d_{k+1} d1dk+1都可能成为最终数字是最高位。为了让结果最小,最高位应该是这些数字中的最小值。
假设找到第一个最小值是 d m d_m dm,则删掉 d 1 ∼ d m − 1 d_1\sim d_{m-1} d1dm1,删掉了 m − 1 m-1 m1个数字,下面还要删掉 k − m + 1 k-m+1 km+1个数字。接下来就是子问题:在 d m + 1 ∼ d n d_{m+1}\sim d_n dm+1dn中删掉 k − m + 1 k-m+1 km+1个数字。
(如果找的不是第一个最小值,那么删掉的数字中也许有与最小值相等的数字可以作为下一位)
该算法最大复杂度: O ( n k ) O(nk) O(nk)

解法2:删掉比后面数字大的数字

考察两个连续数位的数字 d 1 d 2 d_1d_2 d1d2,已知 d 1 > d 2 d_1> d_2 d1>d2
方案一:删掉 d 1 d_1 d1保留 d 2 d_2 d2
方案二:保留 d 1 d_1 d1
两种方案下,最后 d 1 d_1 d1 d 2 d_2 d2会在同一个数位上。很明显应该让更小的 d 2 d_2 d2在这一数位上。
不断用当前数位和下一位比较,如果当前数位大于下一位,那么删除当前位。当前位置指向前一个位置。最后如果剩余几位没删完,则从末尾开始删除这些数位。
该算法最大复杂度: O ( n ) O(n) O(n)
【注意】:去除前导0

【题解代码】

解法1:每次找k+1个数中的最小值
#include<bits/stdc++.h>
using namespace std;
#define N 300
int main()
{
    int k, an = 0; 
    char s[N], a[N];
    cin >> s >> k;
    int len = strlen(s), i;
    for(i = 0; i < len && s[i] == '0'; ++i);//去除前导0 
    while(i < len)
    {
        if(k == len-i)//如果剩下的元素个数只有k个,那么都删掉 
            break;
        int mi = i;//求i~i+k的最小值下标 
        for(int j = i; j <= i+k; ++j)
        {
            if(s[j] < s[mi])
                mi = j;
        }
        a[++an] = s[mi];
        k -= mi - i;//s[i]~s[mi-1]都删掉,删掉了mi-i个元素
        i = mi + 1;
    }
    
    for(i = 1; i <= an && a[i] == '0'; ++i);//去除前导0 
    if(i > an)//如果前导0去完,就没有可以输出的了,说明原来的结果是0 
        cout << "0";
    else
        while(i <= an)
            cout << a[i++];
    return 0;
}
解法2:删掉比后面数字大的数字
#include<bits/stdc++.h>
using namespace std;
#define N 300
int main()
{
    int k, an = 0; 
    char s[N], a[N];
    cin >> s >> k;
    int len = strlen(s), i, ct = 0;
    for(i = 0; i < len && s[i] == '0'; ++i);//去除前导0 
    for(; i < len; ++i)
    {
        while(an > 0 && a[an] > s[i] && ct < k)//a[an]为前一个数 s[i]为后一个数,当a[an]>s[i]时,删除a[an]
        {
            an--;
            ct++;//删除数字个数加1
        }
        a[++an] = s[i];//无论如何s[i]都会填充进数组a
    }
    while(ct < k)//删除末尾的k-ct个元素
    {
        an--;
        ct++;
    }
    for(i = 1; i <= an && a[i] == '0'; ++i);//去除前导0 
    if(i > an)//如果前导0去完,就没有可以输出的了,说明原来的结果是0 
        cout << "0";
    else
        while(i <= an)
            cout << a[i++];
    return 0;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这道题目是一个经典的删数问题,给定一个由数字组成的序列,每次删除其中一个数字,直到序列为空或者只剩下一个数字。要求在删除的过程中,每次删除的数字的下标与前一个删除的数字的下标之差都不等于给定的数m。 我们可以使用模拟的方法来解决这个问题,具体来说,我们可以使用一个数组来存储序列中的数字,然后使用一个指针来记录当前需要删除的数字的下标。每次删除一个数字后,我们可以将指针向后移动m个位置,然后再删除下一个数字,直到序列为空或者只剩下一个数字为止。 需要注意的是,当指针向后移动m个位置后,可能会超出数组的范围,此时我们需要将指针重新指向数组的开头,然后再继续删除数字。 另外,我们还需要记录每次删除的数字的下标,以便最后输出删除的顺序。 最终,我们可以得到一个删除数字的顺序,然后按照这个顺序输出原始序列中的数字即可。 总之,这道题目需要我们使用模拟的方法来解决,需要注意指针的移动和数组下标的处理。 ### 回答2: 删数问题是一个非常经典的问题,也经常出现在编程竞赛中。问题的描述是这样的:给定一个正整数序列,要求将其中K个数字删除,使得得到的剩余的数字组成的新整数最小。该问题的数列可以用数组A来表示,删除的数字个数为K。 解决这个问题的核心思想是贪心。我们可以从左到右扫描整个数列,每次删除比当前数字大的左邻居,直到删除的数字个数达到K为止。具体的算法如下: 1. 创建一个空的数组result来保存最后的结果。 2. 从左到右扫描数组A,对于每个数字A[i],如果result不为空且result的最后一个数字比A[i]大,说明A[i]是一个可以删除的数字,将它从result中删除。 3. 将A[i]添加到result的末尾。 4. 如果删除的数字个数还没有达到K,继续执行步骤2和步骤3。 5. 将result中的数字组合成一个新的整数。 过这个算法,我们可以得到一个最小的新整数。这是因为我们每次都删除了当前位置左边的比它大的数字,使得结果尽可能地小。另外,由于我们从左到右扫描整个数列,所以得到的结果也是按照原数列顺序的。 在实现这个算法时,需要注意的是边界条件。如果K等于0,则不需要删除任何数字,直接将原数列中的数字组合成一个新的整数即可。如果K大于等于原数列的长度,那么删除所有的数字后得到的结果应该是0。 总之,贪心算法,我们可以解决删数问题,得到一个最小的新整数。 ### 回答3: 题目要求解决的是一个删数问题noip1994)。给定一个正整数n,每次删除其中一位数字,直到剩下一个数字为止。我们需要找到最后剩下的数字。 针对这个问题,我们可以使用一个循环队列来解决。首先,将数字1到n依次入队列。然后,每次出队一个数字,将其删除,再将其放回队列的尾部。重复这个过程,直到队列只剩下一个数字为止。最后剩下的数字即为解答。 为了实现该算法,我们可以使用循环队列的数据结构和相应的操作。我们可以使用一个数组来实现队列,同时使用两个指针front和rear来表示队列的头和尾。具体操作如下: 1. 初始化队列,将数字1到n依次放入数组中,并将队头指针和队尾指针分别设置为0和n-1。 2. 当队头指针不等于队尾指针时,重复以下步骤:取出队头数字,将队头指针加1,然后将这个数字删除。 3. 将队头数字加入队尾,并将队尾指针加1。如果队尾指针到达数组的末尾,将其设置为0。 4. 重复步骤2和3,直到队头指针等于队尾指针。此时队列只剩下一个数字,输出该数字。 过以上算法,我们可以很方便地解决这个删数问题。这个算法的时间复杂度为O(n),其中n为给定的正整数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值