P3419 [POI2005]SAMToy Cars / SP688 SAM Toy Cars

一道很妙的贪心题

题面

我们考虑当我们插入时会面临的两种情况

  1. 当地上的玩具,不满 \(k\) 个时,那我们直接放就可以了。

  2. 当满了 \(k\) 个的时候,我们就要从地上拿出一个来给当前的腾位置。

    这就需要我们替换一个,根据我们贪心的思想,当一种玩具出现的比较晚

    的时候,那么我们就可以把它拿走,因为他后面用的次数比较少,这样妈妈

    的移动次数就会少很多 。

那么,我们就有了处理这道题的思路,先求出每个点,他下一次要玩的时间

\(net_i\) 用堆来维护地板上玩具 \(net_i\) 的最大值,来维护上述过程。

坑点

  1. 多测数据一定要清空,最后不要忘记把堆清空

  2. 如果当前这个玩具后面都不会在玩它,我们应该把他的 \(net_i\)
    数组设为一个极大值,而不是0

  3. 如果当前这个点已经在地板上,我们依旧要把它入队,来代替之前的那个玩具。(我在这里卡了好长时间)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
int t,n,k,p,ans,a[500010],last[500010],net[500010];
bool used[100010];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
	return s * w;
}
priority_queue<pair<int,int> >q;
int main()
{
	t = read();
	while(t--)
	{
		memset(last,0,sizeof(last)); memset(used,0,sizeof(used)); //多组数据一定要清空
		n = read(); k = read(); p = read(); ans = 0;
		for(int i = 1; i <= p; i++) a[i] = read();
		for(int i = p; i >= 1; i--)
		{
			if(last[a[i]] == 0) net[i] = 2333333;//如果后面不会在玩这个玩具,我们要把他的 net[i] 数组设为极大值
			else net[i] = last[a[i]];
			last[a[i]] = i;
		}
		for(int i = 1; i <= p; i++)
		{
			if(used[a[i]])
			{
				k++;//如果这种玩具已经在地板上,我们要把他入队来替代之前不优的
				q.push(make_pair(net[i],i));
			}
			else
			{
				if(q.size() < k)//Case1
				{
					q.push(make_pair(net[i],i));
					used[a[i]] = 1; ans++;
				}
				else//Case2
				{
					int t = q.top().second; q.pop();
					used[a[t]] = 0; used[a[i]] = 1; ans++;
					q.push(make_pair(net[i],i));
				}
			}
		}
		printf("%d\n",ans);
		while(!q.empty()) q.pop();//最后记得把堆清空
	}
	return 0;
}

另外,此题还有双倍经验 QAQ。

原文地址:https://www.cnblogs.com/genshy/p/13531789.html