Comet OJ - Contest #10 沉鱼落雁
题目描述
胖头鱼在苦恼“沉鱼落雁”是什么好吃的东西,这很显然是因为他成语没背够。
于是他决定开始背成语。胖头鱼身为鱼界大佬,背成语的姿势自然也和常人不一:
他会先将所有要背的成语一字排开,比较难背的成语会重复出现,最多重复 33 次 (也就是出现次数可能为 11, 22 或 33)。这样就得到了一个可能有重复元素的成语序列,然后他会将这个序列打乱顺序,并按打乱后的顺序背下去。为了均匀的背所有成语,提高背成语的效率,相同成语在打乱后的序列中出现位置的最小间隔自然是越大越好。 (编号为 aa 和 bb (a<ba<b) 的两个位置的间隔定义为 b-a-1b−a−1)
现在胖头鱼把打乱前的成语序列给你,你需要帮他打乱顺序,使得相同成语的最小间隔最大。
你不需要输出确切的方案,仅求出最小间隔的最大值即可。
特别地,当每种成语都只出现一次时,把最小间隔的最大值视为nn。
输入描述
第一行一个整数n(1<=n<=1e5),表示成语序列长度为 n。同一个成语最在序列中最多出现 3 次。
接下来 n 个整数 a1, a2, ……, an(1<=ai<=1e9) 表示一个成语序列,每个正整数都代表一个成语,两个成语不同当且仅当值不同。
输出描述
输出一个整数,表示最小间隔的最大值。
样例输入 1
9
5 4 3 1 3 1 1 5 5
样例输出 1
2
样例解释 1
其中一組可行方案是1 3 5 1 3 5 1 4 5,容易验证没有比 22 更大的答案。
样例输入 2
5
1 2 3 4 5
样例输出 2
5
题目一开始很懵,后来发现最大距离就是个数为2的种类加个数为3的种类减1,即:ans=m1+m2-1,个数为1的种类只需补给m1和m2即可,需特判一下3的种类为0和输出为n的情况:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100005];
map<int,int>p;//计数
map<int,int>v;//标记
int main()
{
int n;
cin>>n;
int num=0;
int m1=0;//3的种类
int m2=0;//2的种类
int m3=0;//1的种类
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(p.count(a[i])==0) {p[a[i]]=1; num++;v[a[i]]=1;}
else p[a[i]]++;
}
for(int i=0;i<n;i++){
if(p[a[i]]==3 && v[a[i]]) {m1++; v[a[i]]=0;}
else if(p[a[i]]==2 && v[a[i]]) {m2++; v[a[i]]=0;}
else if(p[a[i]]==1 && v[a[i]]) {m3++; v[a[i]]=0;}
}
if(num==n) cout<<n;
else if(m1==0) {cout<<m2+m3-1;}
else if(m1){
int k=m3/3,i=0,j=0;
if(m3==1) {m1++;m2--;}
else
{
for(i=0;i<=k;i++)
{
if((m3-i*3)%2==0)
{
j=(m3-i*3)/2;
break;
}
}
m1+=i;
m2+=j;
}
cout<<m1+m2-1;
}
return 0;
}