1. 程式人生 > 其它 >YBTOJ記憶體管理(堆的應用)

YBTOJ記憶體管理(堆的應用)

題面


題目分析

這題最大的特點就是細節很多,注意審題。
(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)設定一個額外的陣列,來完成對當前無法取出的點的更新,並在它被取出時用這個陣列對其判斷是否合法,或者更新。