hiho學習日記:hiho一下 第六十二週

http://hihocoder.com/contest/hiho62/problem/1

題意分析

在瀏覽網頁的時候,緩存技術能夠迅速地顯示頁面。這裏我們對瀏覽器的緩存技術進行簡化:我們認爲瀏覽器的緩存大小爲M,表示緩存可以存儲M個頁面。
當用戶訪問URL時,瀏覽器會先到緩存中查詢是否有該頁面的記錄,如果有則直接從緩存中提取數據;否則,會發送網絡請求,從Internet獲取該頁面,並將該頁面放入緩存中。當緩存中的頁面數量超過M時,緩存技術會替換掉緩存中最不頻繁訪問的網頁,即最後訪問時間最早的頁面。
現在,我們希望對於給定的用戶訪問歷史記錄,請判斷出每一次訪問頁面是從緩存中讀取的,還是通過網絡請求獲得的。(緩存最開始數據爲空)

算法分析

本題的實質是模擬緩存的工作。

簡單的算法

我們直接純粹的模擬緩存,每當訪問一個新的頁面時,我們去遍歷現在的m條緩存記錄。一是確認當前瀏覽的頁面URL是否存在於緩存記錄中,二是找出當前緩存記錄中最早的一條,並將其替換。
每一次操作的時間複雜度爲O(M),由於一共有N條記錄,所以總的時間複雜度爲O(NM)。
優化
首先我們可以知道O(NM)中的N是沒有辦法優化的,所以我們只能從每一次緩存更新入手。
緩存的更新分爲兩個部分:
檢查當前是否存在於緩存
將緩存記錄中最早的一條進行替換
檢查當前頁面是否存在於緩存
這一個部分我們可以簡單的使用map來實現,簡單的用Boolean(bool)類型來表示該URL是否在緩存中即可。
將緩存記錄中最早的一條進行替換
之前我們採取的方法是通過O(M)的時間進行遍歷來查找,如果我們將緩存換用另一種數據結構來進行存儲就可以做到O(logN)。
比如二叉樹、小根堆等都可以做到。

在這裏打算講的是一種只使用map來完成緩存的更新。
首先我們用a[1…n]來表示瀏覽網頁的記錄,用map< url, int > lastVisit的方式來對URL進行記錄。
lastVisit[ a[i] ]表示a[i]這條url最後一次訪問時對應的時間。
我們知道緩存的大小爲M,而且滿足了緩存中一定是M個不相同的URL。
假設在我們現在的緩存中最早的一條記錄對應的爲a[s],最新的一條記錄對應的是a[t]。這樣對於第一個問題,檢查當前頁面是否存在於緩存,我們只需要判定當前i是否在[s,t]這個區間中。
同時可以證明在a[s…t]中至多包含有m條不同的URL。
其證明:

假設a[s..t]中包含有m+1條不同的記錄,則我們加入第m+1條記錄時,在緩存中是無法找到已經存在的記錄。我們一定會拋棄掉緩存中最早的那條記錄a[s],並將其替換爲第m+1條記錄。所以a[s..t]中一定至多包含m條不同的URL記錄。

對於s,t的維護:
t一定總是等於i的,因爲a[i]一定是最新的訪問記錄。
而對於s,我們一定有 lastVisit[ a[s] ] == s。因爲若lastVisit[ a[s] ] = k,則一定有k>s,表示該URL在[s+1,t]之中仍存在。若lastVisit[ a[s] ] = s,則表示在[s+1,t]這個區間內都沒有a[i]的訪問記錄了,a[s]則一定是[s,t]這個區間中最早的訪問記錄。
並且,若此時將a[s]從區間[s,t]中拋棄則表示將這個URL從緩存中拋棄。
所以我們總是要維護lastVisit[ a[s] ] == s。

我們舉個例子來說明:

在這裏插入圖片描述

該樣例中n=9, m=3。小寫的abcd表示4個不同的url。左邊表示我們的訪問記錄,右邊表示我們的LastVisit。
當進行到某一步時,S=1,T=5。
此時我們移動T+1,由於第6個網頁爲d,並不在[s,t]中且m已經到達上限,所以我們需要將a彈出,s+1。同時更新LastVisit[d],得到中間的一步。
接着我們需要維護s,由於LastVisit[b] = 4, LastVisit[c] = 5,所以2,3位的b和c會被跳過,直到s=4,此時有LastVisit[b] = 4。
這樣得到最下面的一幅圖,[s,t]完成了更新。
僞代碼

s = 1
For i = 1..n
    If (lastVisit[ a[i] ] in [s,i])
        Print "Cache"
    Else
        cacheCnt = cacheCnt + 1;
        If (cacheCnt > m)        // 緩存已滿
            s = s + 1            // 將該a[s]從cache中拋棄         
        End If
        Print "Internet"
    End
    lastVisit[ a[i] ] = i    // 更新最後一次訪問時間
    While (lastVisit[ a[s] ] != s) 
        s = s + 1
    End While                // 更新s找到新的最早的訪問記錄  
End For

時間複雜度方面,由於使用了map來維護,所以總的時間複雜度爲O(NlogN)
結果分析
該題目的現場通過率爲37.5%。200人提交通過了75名選手。
作爲該場比賽的第一題,通過率並不是特別高。而本題實際的難度並不大,如果直接從複雜度分析入手應該也能夠很容易推到出可行的辦法。

AC代碼:

#include <bits/stdc++.h>

using namespace std;
#define LL long long
const int Mod = 1e9 + 7;
const int maxn = 2e4 + 5;
const double eps = 0.00000001;
const int INF = 0x3f3f3f3f;

map<string, int> mp;
string st[maxn];
int main()
{
    int n, m;
    cin >> n >> m;
    int s, cacheCnt = 0;
    s = 1;
    for (int i = 1; i <= n; i ++) {
        cin >> st[i];
        //if(!mp[st[i]]) mp[st[i]] = i;
        if(mp[st[i]] < i && mp[st[i]] >= s) {
            cout << "Cache\n";
        }else {
            cacheCnt ++;
            if(cacheCnt > m) s = s + 1;
            cout << "Internet\n";
        }
        mp[st[i]] = i;
        while(mp[st[s]] != s) s ++;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章