poj 1989 & hdu 2712 & 洛谷5093 题解(贪心,思维,好题)

博客介绍了如何使用贪心策略解决一道编程题目,涉及寻找序列中一个最短的、所有数在[1,k]范围内未出现的子序列。通过将原序列分组并确保每个组包含[1,k]的所有数,证明了分组方法的正确性,并给出O(n)的解决方案,最后展示了符合要求的序列构造方法。" 129557065,13633813,Virtualbox与VMware安装Ubuntu及MongoDB的困扰,"['虚拟化技术', 'Linux运维', 'Ubuntu安装', 'MongoDB安装']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:
poj
hdu
洛谷

题意简述

给定一个序列,长度为 n ( &lt; = 1 e 5 ) n(&lt;=1e5) n(<=1e5),所有数都在 [ 1 , k ] [1,k] [1,k]范围内。请你在序列中找到一个长度最短的没有出现的子序列(不一定连续的那种)。

思路

题解里面有个奆佬这么说的:

我们把原序列分组(一组是一段连续的子序列),一个组里包含 [ 1 , k ] [1,k] [1,k]中的所有数。

不知道这个奆佬是怎么想到这个东西的。奆佬太强了%%%
加上一些必要的注释:

第一组的左端点是 1 1 1,在分组过程中,左端点=上一个组的右端点 + 1 +1 +1,在包含 [ 1 , k ] [1,k] [1,k]所有数的前提下,保证右端点最小(就是找到第一个满足的就 b r e a k break break)。

比如说样例是 1 , 5 , 3 , 2 , 5 , 1 , 3 , 4 , 4 , 2 , 5 , 1 , 2 , 3 1,5,3,2,5,1,3,4,4,2,5,1,2,3 1,5,3,2,5,1,3,4,4,2,5,1,2,3,分组就是
[ 1 , 5 , 3 , 2 , 5 , 1 , 3 , 4 ] , [ 4 , 2 , 5 , 1 , 2 , 3 ] [1,5,3,2,5,1,3,4],[4,2,5,1,2,3] [1,5,3,2,5,1,3,4],[4,2,5,1,2,3]

这个分组过程是 O ( n ) O(n) O(n)的。然后由于我们保证了右端点最小,所以,每个组的最后一个数在这个组中肯定是有且仅有 1 1 1次出现。

简单的证明:如果最后一个数 x x x有多次出现,除了在最后一个位置,在前面也有几次。那为什么我们要选这最后一个位置呢?我们珂是要保证右端点最小的!综上,没有这样的 x x x,证毕。

然后最后珂能还剩下一些东西分不了组(或者没有),这样它们就一定会没有某个数。我们把每个组的最后一个数取出来,然后在来一个最后剩余中没有的数,组成一个序列,肯定不是原序列的子序列。并且这个序列的长度是组数 + 1 +1 +1

证明这个序列不是原序列的子序列:
设有 g g g个组,第 i i i个组跨的区间是 [ l i , r i ] [l_i,r_i] [li,ri],原序列叫 a a a,我们构造出来的序列叫 b b b
b b b的前 g g g个数在前 g g g个组中,上已证,只有一次出现。那么, b b b的前 g g g个数就能 跳 过 前 k 个 组 \color{#ff0000}跳过前k个组 k,然后最后选的那个数是剩下的数没有的,就把剩下的数也跳过了,而且和前面那个“跳过”还不太一样,前面是找到了跳过,这个是没找到跳过。那么就找不到匹配。所以,我们选的 b b b是满足条件的。而非常显然,没有比 b b b更短的串了。所以答案就是 g + 1 g+1 g+1

思维过程和证明不少,但是具体事项的话,我们只要 O ( n ) O(n) O(n)求出 g g g,然后把它 + 1 +1 +1就珂以了。

代码:
(顺便说一句,毒瘤hdu是多组数据的!!!而且还没说!!!)
(这里的代码是 3 3 3 O J OJ OJ都能 A C AC AC的代码)

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 100100
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    int n,k;
    int a[N];
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        for(int i=1;i<=n;++i)
        {
            R1(a[i]);
        }
    }
    int cnt[N/5];//常数小一点
    void Soviet()
    {
        FK(cnt);
        int have=0;
        int group=0;
        for(int i=1;i<=n;++i)
        {
            if (cnt[a[i]]==0) ++have;
            ++cnt[a[i]];
            if (have==k)//刚到满足的时候,就要存储下这个组,然后初始化去找下一个
            {
                ++group;
                have=0;
                FK(cnt);
            }
        }
        printf("%d\n",group+1);
    }
    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        while(scanf("%d%d",&n,&k)>0)
        {
            Input();
            Soviet();
        }
    }
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值