【BZOJ1826】【洛谷P4404】缓存交换【贪心】【堆】

题目大意:

题目链接:
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=1826
洛谷:https://www.luogu.org/problemnew/show/P4404

在计算机中,CPU只能和高速缓存Cache直接交换数据。当所需的内存单元不在Cache中时,则需要从主存里把数据调入Cache。此时,如果Cache容量已满,则必须先从中删除一个。 例如,当前Cache容量为3,且已经有编号为10和20的主存单元。 此时,CPU访问编号为10的主存单元,Cache命中。 接着,CPU访问编号为21的主存单元,那么只需将该主存单元移入Cache中,造成一次缺失(Cache Miss)。 接着,CPU访问编号为31的主存单元,则必须从Cache中换出一块,才能将编号为31的主存单元移入Cache,假设我们移出了编号为10的主存单元。 接着,CPU再次访问编号为10的主存单元,则又引起了一次缺失。我们看到,如果在上一次删除时,删除其他的单元,则可以避免本次访问的缺失。 在现代计算机中,往往采用LRU(最近最少使用)的算法来进行Cache调度——可是,从上一个例子就能看出,这并不是最优的算法。 对于一个固定容量的空Cache和连续的若干主存访问请求,聪聪想知道如何在每次Cache缺失时换出正确的主存单元,以达到最少的Cache缺失次数。


思路:

首先,当Cache满了之后,选择已有单元中下一次出现位置最后的删除是最优秀的。因为删除的位置越后,中间可以避免其他的缺失个数就越多,相对的缺失次数就会越少。
维护next[x]next[x]表示下一个和元素a[x]a[x]相同的元素位置。这个是可以O(n)O(n)求出来的。
然后我们要维护多个二元组(x,i)(x,i),其中ii表示选择第ii个位置的元素,xx表示next[i]next[i]。我们要在选择的不超过mm个二元组中选择xx尽量大的删除。所以可以用优先队列来维护。
注意到若两个元素x,yx,y相同,设xx的位置在yy后面(xx入队时间比yy晚),那么必然有next[x]>next[y]next[x]>next[y]。所以其实可以不用删除前面的yy,因为xx必然可以覆盖掉yy,在yy的前面出队。
时间复杂度O(nlogn)O(nlog n)


代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mp make_pair
using namespace std;

const int N=100010;
int n,m,tot,ans,sum,a[N],b[N],last[N],next[N];
priority_queue<pair<int,int> > q;
bool inque[N];

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+1+n);
	tot=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;i++)
		a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
	memset(last,0x3f3f3f3f,sizeof(last));
	for (int i=n;i>=1;i--)
	{
		next[i]=last[a[i]];
		last[a[i]]=i;
	}
	for (int i=1;i<=n;i++)
	{
		if (ans<m && !inque[a[i]])
		{
			ans++;
			inque[a[i]]=1;
		}
		else if (ans>=m && !inque[a[i]])
		{
			ans++;
			inque[a[i]]=1;
			inque[q.top().second]=0;
			q.pop();
		}
		q.push(mp(next[i],a[i]));
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998101.html