BZOJ-1206 虚拟内存 Hash+离散化+Priority_Queue

闻说HNOI每年都有一道Hash。

1206: [HNOI2005]虚拟内存
Time Limit: 50 Sec Memory Limit: 162 MB
Submit: 330 Solved: 202
[Submit][Status][Discuss]

Description
操作系统中一种重要的存储管理技术就是虚拟内存技术。操作系统中允许进程同时运行,也就是并行。每个进程都有其相对独立的数据块(进程运行的过程中将对其进行读写操作)。理想的情况下,这些数据块都应该存放在内存中,这样才能实现高效的读写操作。但事实上,内存的容量有限,每个进程只能把一部分数据放在内存中,为了解决这个矛盾,提出了虚拟内存技术。虚拟内存技术的基本原理是:对进程而言,内存空间是无限大的,进程可以随意地读写数据,而对操作系统内部而言,利用外存来模拟扩充的内存空间,进程要求访问某个内存单元时,交由操作系统处理,操作系统首先在内存中查找该单元是否存在,如果存在,查找成功,否则转入外存查找(一定存在于外存中)。就存储介质的物理性质而言,内存的访问速度相对于外存要快得多,因此对于每个进程来说操作系统应该把那些访问次数较多的数据存放在内存中,而把那些访问次数很少的数据放在外存中。如何选择内存中暂留的数据是一个很值得研究的问题,下面介绍一个内存管理中比较常用的算法:内存中的数据以页为基本存储单位,进程的读写操作都针对页来进行。实际内存空间被分割成n页,虚拟内存空间的页数往往要多得多。某一时刻,进程需要访问虚存编号为P的页,该算法的执行步骤如下: a. 首先在内存中查找,如果该页位于内存中,查找成功,转d,否则继续下面的操作; b. 寻找内存中是否存在空页(即没有装载任何数据页的页面),若有,则从外存中读入要查找页,并将该页送至内存中的空页进行存储,然后转d,否则继续下面的操作; c. 在内存中寻找一个访问次数最少的页面(如果存在多个页面的访问次数同时为最少,则选取最早读入数据进入内存的那个页面),从外存中读入要查找页,替换该页。 d. 结束所谓访问次数是指从当前页面进入内存到该时刻被访问的次数,如果该页面以前进入过内存并被其它页面替换,那么前面的访问次数不应计入这个时刻的访问次数中。你的任务是设计一个程序实现上述算法。测试数据将会提供m条读写内存的命令,每条命题提供要求访问的虚拟内存页的编号P。你的程序要求能够模拟整个m条命令的全部执行过程,所有的命令是按照输入的先后执行的,最开始的时候内存中的n页全为空。

Input
第1行为n<10000和m<1000000,分别表示内存页数和读写内存命令条数。接下来有m行,其中第i+1行有一个正整数Pi<=10^9,表示第i条读写内存命令需要访问的虚拟内存页的编号。

Output
仅包含一个正整数,表示在整个模拟过程中,在内存中直接查找成功的次数(即上面的算法只执行步骤a的次数)。

Sample Input
3 8
1
1
2
3
4
2
5
4

Sample Output
1

HINT

Source
hash

这道题目,算是模拟吧,调的时候有一个点忘了,WA了两次,无果后,遂寻男神xym,他竟然用map!!!不过还是看出来了问题,也不算坑。

数据是10^9级的,但实际数量10^6级,所以想到离散;用堆维护,记录三个值:离散后的数值,访问次数,插入堆的顺序;当堆中多于n后,进行替换;值得一提的是要重载运算符(开始自己重载的有点丑,于是借鉴了男神的讨论打了个opreator)

有趣的是,在我调试的时候,电脑弹出了一个提示:
这里写图片描述
真机智QAQ

下面是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int nc[1000010];
int n,m;
int p[1000010],pi[1000010];
int num=0;int cnt;
int ans=0;
struct data{
    int dat,tim,sx;//dat是表示值,tim表示访问次数,sx表示先后顺序 
    bool operator > (const data  &other) const
        {
            if (tim<other.tim) return 0;
            if (tim>other.tim) return 1;
            return sx>other.sx; 
        }
};
priority_queue <data,vector<data>,greater<data> > q;

//再开一个数组,专门存放在hash表里的位置

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

//查找内存,查找成功,结束,答案+1 
//查找失败,查找空页,有空页,插入空页中 结束 
//无空页,删除访问数最小的页,插入此页 结束 

bool cmp(int a,int b)
{
    return a<b;
}
int erfen(int x)
{
    int left=1,right=cnt;
    while (left<=right)
        {
            int mid=(left+right)>>1;
            if (p[mid]==x) return mid;
            if (p[mid]>x)  right=mid-1;
                    else   left=mid+1;
        }
}
void lsh()
{
    sort(p+1,p+m+1,cmp);
    cnt=1;
    for (int i=2; i<=m; i++)
        if (p[i]!=p[i-1]) p[++cnt]=p[i];
    for (int i=1; i<=m; i++)
        pi[i]=erfen(pi[i]);             
} 

int main()
{
    n=read();m=read();
    for (int i=1; i<=m; i++)
        {p[i]=read();pi[i]=p[i];}
    lsh();
    for (int i=1; i<=m; i++)
        {
            int now=pi[i];
            int tmp;
            nc[now]++;
            if (nc[now]>1) {ans++;continue;}//command a.
            data x;
            x.dat=now,x.tim=1,x.sx=i;
            if (q.size()<n) {q.push(x);continue;}//command b.
            data y;
            y=q.top();
            while (y.tim!=nc[y.dat])
                {
                    q.pop();
                    y.tim=nc[y.dat];
                    q.push(y);
                    y=q.top();
                }
            nc[y.dat]=0;
            q.pop();q.push(x);//command c.          
        }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346220.html