学到了点新东西就把这道题写个题解,防止我自己忘了,这场题目尚可,无奈我技不如人写的是依托答辩,就写个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();
}
}