2023寒假集训营2 H-Tokitsukaze and K-Sequence

学到了点新东西就把这道题写个题解,防止我自己忘了,这场题目尚可,无奈我技不如人写的是依托答辩,就写个H的题解吧。

题面:

链接:https://ac.nowcoder.com/acm/contest/46810/H

来源:牛客网

Tokitsukaze 有一个长度为 nnn 的序列 a,她想把这个序列划分成 k个非空子序列。定义序列的值为这个序列中只出现一次的数字的个数。

对于 k=1.....n,Tokitsukaze 想知道把序列 a划分成 k个非空子序列后,所有子序列的值的和最大是多少。

请注意,子序列不一定是连续的。

题目意思很明确了,就是找数字的出现次数。

思路:我们可以发现每一轮都可能会有数字可能只出现一次,比如第一轮只有一个序列,那么只出现一次的数字的个数可以全部算上,然后第二轮时是分出两个子序列,不要求连续,那么只出现两次的数字就可以被拆散到两个序列中,这时总序列值会加2,大于两次的数字,可以把一个数放在只出现一次的序列中,其它就放在另一个序列中,这样总序列值就会加1。换句话说就是用贪心的思想,第二轮的时候把只出现一次的数字作为一个序列,然后大于一的数字分出一个数字放到只出现一次的数字序列中,其他剩余的数就放入另一个序列中,如果出现次数正好是2的话,那么正好在每个序列中都只出现了一次所以贡献是2,大于两次的数的贡献是1。

那么我们就能根据这些写代码了,这里学到新东西就是结构化绑定,能很方便的遍历map这个数据结构。

AC code:

#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
#include<vector>
#include<cstring>
#include<map>
using namespace std;
const int manx = 2e5 + 10;
typedef long long ll;
int a[manx];
int f[manx];
ll deep[manx];
void solve()
{
    int n;
    cin >> n;
    map<int, int>mp;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        mp[a[i]]++;
    }
    ll ans = 0;
    for (auto [i, j] : mp)//结构化绑定,从头遍历map
    {
        if (j == 1)//如果出现次数为1,一开始的总序列值就加一
        {
            ans++;
        }
        //cout << i << " " << j << endl;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << ans << "\n";
        vector<int>v;
        for (auto [x, y] : mp)
        {
            v.push_back(x);
        }
        for (auto x : v)
        {
            if (mp[x] == 1)
            {
                mp.erase(x);//如果出现次数已经为1,那么就表示该数已经贡献完毕,不会再对该数进行序列分配了,已经完成序列分配了
                continue;
            }
            mp[x]--;//每轮都会分配出一个序列
            ans++;
            if (mp[x] == 1)//如果分配完序列后正好等于1,说明还能有1贡献。
            {
                ans++;
            }
        }
    }

}

int main()
{
    int t;
    cin >> t;
    //t = 1;
    while (t--)
    {
        solve();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值