YBTOJ記憶體管理(堆的應用)
阿新 • • 發佈:2021-09-13
題面
題目分析
這題最大的特點就是細節很多,注意審題。
(1)如果被佔用,則必須過600s(包括當前這一秒)才能恢復空的狀態。
而如果這期間記憶體被查詢了,則需要再過600s才能被再次使用。
(2)如果一個記憶體沒有被佔用,則查詢不會將它變為佔用的狀態。
(3)發出佔用一個記憶體的請求時,優先佔用編號最小的記憶體點。
演算法設計:
寫兩個小根堆,第一個堆heap1儲存未被佔用的點的編號,第二堆heap2儲存被佔用的點開始被佔用的時間。
每次輸入得到一個當前時間n,則不斷地取出heap2的堆頂,若堆頂已經不再被佔用,則將其插入heap1中,直到出現一個仍被佔用的點,則迴圈結束。
然後再判斷要求的操作,若為請求佔用空間,則取出heap1堆頂,插入heap2中,若為判斷,則用vis陣列處理。
然後就會發現我們忘記了一個問題:
如果一個點在仍被佔用時查詢器狀態,就會把它需要等待的時間恢復到600s,但這個點不一定是heap2堆頂,我們也就無法取出它並且進行修改。
但是不需要取出啊,我們可以再用一個數組change[i]表示一個點在被佔用的時候被查詢的時間點,每次在取出heap2堆頂的過程中,對每一個取出的點判斷其change陣列是否合法,若不合法,則令其time=change,完成更新,插回heap2中。
Code
#include<bits/stdc++.h> using namespace std; const int maxn=3e4+40; int len1,len2,heap1[maxn]; int change[maxn]; bool vis[maxn]; struct mint { int id,time; }heap2[maxn]; void insert1(int k) { heap1[++len1]=k; int pla=len1; while(pla>1) { int fa=pla/2; if(heap1[fa]<heap1[pla]) break; swap(heap1[fa],heap1[pla]); pla=fa; } } int get1() { int res=heap1[1]; heap1[1]=heap1[len1--]; int pla=1; while(pla*2<=len1) { int son=pla*2; if(son+1<=len1&&heap1[son]>heap1[son+1]) son++; if(heap1[pla]<heap1[son]) break; swap(heap1[son],heap1[pla]); pla=son; } return res; } void insert2(mint k) { heap2[++len2]=k; int pla=len2; while(pla>1) { int fa=pla/2; if(heap2[fa].time<heap2[pla].time) break; swap(heap2[fa],heap2[pla]); pla=fa; } } mint get2() { mint res=heap2[1]; heap2[1]=heap2[len2--]; int pla=1; while(pla*2<=len2) { int son=pla*2; if(son+1<=len2 && heap2[son].time>heap2[son+1].time) son++; if(heap2[pla].time < heap2[son].time) break; swap(heap2[pla],heap2[son]); pla=son; } return res; } int main() { int n,q; char a; for(int i=1;i<=30000;++i) insert1(i); while(scanf("%d",&n)!=EOF) { while(1) { if(len2==0) break; mint g=get2(); if(g.time+600>n) { insert2(g); break; } if(change[g.id]+600>n) { g.time=change[g.id]; insert2(g); } else { vis[g.id]=false; insert1(g.id); } } cin>>a; // printf("%c\n",a); if(a!='.') { int k=get1(); vis[k]=true; mint g; g.id=k; g.time=n; insert2(g); printf("%d\n",g.id); } else { scanf("%d",&q); if(vis[q]) { printf("+\n"); change[q]=n; } else printf("-\n"); } } return 0; }
總結:
根據這個題的分析過程,以及對無法取出的點的更新方式,我們可以得出一些比較普遍性的思路:
(1)多個堆維護多種資料,完成複雜的修改過程。
(2)設定一個額外的陣列,來完成對當前無法取出的點的更新,並在它被取出時用這個陣列對其判斷是否合法,或者更新。