【貪心】【JSOI2010】快取交換
快取交換
【問題描述 】
在計算機中,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缺失次數。
【輸 入】
輸入檔案第一行包含兩個整數N和M(1<=M<=N<=100,000),分別代表了主存訪問的次數和Cache的容量。
第二行包含了N個空格分開的正整數,按訪問請求先後順序給出了每個主存塊的編號(不超過1,000,000,000)。
【輸 出】
輸出一行,為Cache缺失次數的最小值。
【樣 例 】
輸入資料 |
輸出資料 |
解釋 |
1 2 3 1 2 3 |
4 |
在第4次缺失時將3號單元換出Cache。 |
1 3 4 2 5 3 1 4 5 6 |
7 |
【思路】貪心,用next[i]陣列儲存第I個記憶體地址的下一次出現的位置,維護一個大根堆(我用優先佇列實現= =剛好今天剛學)
要被換掉的這個數下一次出現的位置一定是越遠越優
於是得到以下策略:
如果當前命中,就把容器容量+1,push進當前點的next[i]值//因為優先佇列沒辦法找到當前值在佇列中的位置= =想出了這種奇葩的方法,居然過了ORZ,正確性好像不大能保證
如果容器容量有剩餘,直接push進去
如果需要交換出cache,彈出堆頂元素,並把當前點push進去
mark陣列標記當前值是否在堆中
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,m,f[100000],next[100000],last[10000000],mark[10000000],i,ans;
typedef pair<int,int> ele;
int main()
{
// freopen("swap.in","r",stdin);
// freopen("swap.out","w",stdout);
scanf("%d%d",&n,&m);
memset(mark,0,sizeof(mark));
memset(next,1,sizeof(next));
for (i=1;i<=n;i++){
scanf("%d",&f[i]);
next[last[f[i]]]=i;
last[f[i]]=i;
}
priority_queue<ele> q;
for (i=1;i<=n;i++){
if (mark[f[i]]) { q.push(make_pair(next[i],f[i])); m++; continue;}
if (q.size()<m)
{ q.push(make_pair(next[i],f[i])); ans++; mark[f[i]]=1; continue; }
ele k=q.top();
q.pop();
mark[k.second]=0;
q.push(make_pair(next[i],f[i]));
mark[f[i]]=1;
ans++;
}
printf("%d\n",ans);
// getchar(); getchar();
}