cf864 Round #436 Div2-D【贪心】

这篇博客讨论了一种算法问题,即如何通过最少的步骤将给定的n个数转换成字典序最小的组合。作者提出了一种解决方案,利用优先队列保存未出现过的数,并按字典序依次替换出现多次的数,确保字典序最小。在遇到小于优先队列头部的数时,会固定该位置并标记,后续相同数也会被替换,以保证字典序的最小化。代码实现中展示了详细的处理流程。

Date:2021.12.30

题意:给定n个数,要将其变成①n个数的组合 ②字典序最小,最少几步。

思路:我们无非是将出现过>=2次的数变成别的数,而变成的数一定是未在原序列中出现过的,因此与原数只能是>或<关系,因此我们将没出现过的数存起来,用一个优先队列保证最小的在最前面,这样挨个更换完后字典序一定是最小的。但还有个问题,我们找到了当前出现多次的数,如果它 > 优先队列队头元素,那么一定会被换掉;但如果 < 怎么办?那么当前元素就固定位置在这里,并且标记一下这个元素已经用过,接着找即使再找到这个已标记元素值相同的元素,都给它更换掉,这样一定能保证字典序最小,换掉的一定是最恰当的且不会颠倒。
代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5+10;
LL n,m,k,t;
LL a[N],cnt[N];
bool st[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        cnt[a[i]]++;
    }
    int ans=0;queue<LL>q;
    for(int i=1;i<=n;i++)
        if(cnt[i]==0) q.push(i);
    for(int i=1;i<=n;i++)
    {
        if(cnt[a[i]]>1)
        {
            if(a[i]<q.front()&&!st[a[i]])
            {
                st[a[i]]=true;
                continue;
            }
            else 
            {
                cnt[a[i]]--;
                a[i]=q.front();q.pop();
                ans++;
            }
        }
       
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;i++) cout<<a[i]<<' ';
    return 0;
}
graph TD subgraph 哈夫曼树 (Huffman Tree) A(100) --> B(40) A --> C(60) B --> D(19) B --> E(21) C --> F(28) C --> G(32) F --> H(11) F --> I(17) H --> J(5) H --> K(6) I --> L(7) I --> M(10) J --> N(2) J --> O(3) end style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#ccf,stroke:#333,stroke-width:2px style C fill:#ccf,stroke:#333,stroke-width:2px style D fill:#9cf,stroke:#333,stroke-width:2px style E fill:#9cf,stroke:#333,stroke-width:2px style F fill:#9cf,stroke:#333,stroke-width:2px style G fill:#9cf,stroke:#333,stroke-width:2px style H fill:#69f,stroke:#333,stroke-width:2px style I fill:#69f,stroke:#333,stroke-width:2px style J fill:#69f,stroke:#333,stroke-width:2px style K fill:#69f,stroke:#333,stroke-width:2px style L fill:#69f,stroke:#333,stroke-width:2px style M fill:#69f,stroke:#333,stroke-width:2px style N fill:#36f,stroke:#333,stroke-width:2px style O fill:#36f,stroke:#333,stroke-width:2px linkStyle 0 stroke-width:2px,fill:none,stroke:black; linkStyle 1 stroke-width:2px,fill:none,stroke:black; linkStyle 2 stroke-width:2px,fill:none,stroke:black; linkStyle 3 stroke-width:2px,fill:none,stroke:black; linkStyle 4 stroke-width:2px,fill:none,stroke:black; linkStyle 5 stroke-width:2px,fill:none,stroke:black; linkStyle 6 stroke-width:2px,fill:none,stroke:black; linkStyle 7 stroke-width:2px,fill:none,stroke:black; linkStyle 8 stroke-width:2px,fill:none,stroke:black; linkStyle 9 stroke-width:2px,fill:none,stroke:black; linkStyle 10 stroke-width:2px,fill:none,stroke:black; linkStyle 11 stroke-width:2px,fill:none,stroke:black; linkStyle 12 stroke-width:2px,fill:none,stroke:black; linkStyle 13 stroke-width:2px,fill:none,stroke:black; linkStyle 14 stroke-width:2px,fill:none,stroke:black; subgraph 字符与权值 D --- b(19) E --- g(21) G --- e(32) K --- d(6) L --- a(7) M --- h(10) N --- c(2) O --- f(3) end
11-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值