题目大意比较长,我几句话说不出来。
下面是摘的别人的题目大意:
你有一个能放M(M <= 50000)个物品的容器,你可能会遇到N(N <= 100000)个物品。
规则:
1.遇到的物品K你如果之前没有遇到过,且你的容器未满,那么把这个物品(编号K),放进容器中,并记录数字1(相当于权值),且记录遇到该物品的时间t。
2.遇到的物品K如果在你的容器里,那么仅仅把它的标记数字(即所谓权值+1)。
3.遇到的物品K你如果之前没有遇到过,且你的容器已满,那么就把容器里标记数字最大的容器移除,如果不止一个,那么就把最先进入容器的那一个移除。(移除的物品就相当于没有遇到过,之后遇到算第一次遇到。)
求解问题:给你M,N,以及一行数字N个K(i),(K(i)< 2^20,i = 1 to N),输出共发生移除操作多少次。(TimeLimit:1s)
题解:
此题目是一个模拟题,关键在于找到高效的模拟方法。
最方便的是用set+重载set的比较函数,使得set中的排序是先按照l[i]排序,l[i]==l[j]时再按照装入背包的时间排序。(可以看作是一个可修改堆)
用set时有一些需要注意的地方,见程序。
#include<iostream>
#include<set>
#include<cstdio>
#include<cstring>
using namespace std;
int n,ans;
unsigned int m;
int l[2000000];//原题目中的l
int t[2000000];//物品x装入背包的最早时间
int a[200000];
struct cmp//重载比较函数
{
bool operator()(int x,int y)const
{
if(l[x]<l[y])return true;
else if(l[x]==l[y]&&t[x]>t[y]) return true;
return false;
}
};
set<int,cmp> myset;
set<int,cmp>::reverse_iterator it;
int main()
{
int sec=0;
while(scanf("%u%d",&m,&n),m||n)
{
sec++;ans=0;
myset.clear();
memset(l,0,sizeof(l));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
if(myset.count(a[i])==0)
{
if(myset.size()<m)
{
l[a[i]]=1;t[a[i]]=i;myset.insert(a[i]);
}
else
{
ans++;
it=myset.rbegin();
myset.erase(*it);
l[a[i]]=1;t[a[i]]=i;myset.insert(a[i]);
}
}
else
{//注意l[a[i]]发生变化后要先删除原来的a[i],再插入a[i]。(否则会打乱顺序)
myset.erase(a[i]);
l[a[i]]++;
myset.insert(a[i]);
}
}
printf("Case %d: %d\n",sec,ans);
}
return 0;
}