uva 1591 Data Mining

紫書第四章,最有毒的一題,題目意思有毒,翻譯有毒,做法有毒。


搬運一篇別人的題解吧,我也是照着別人寫的


只要暴力所有A、B的組合,看解是不是可行的並找出最優解。


       (設有n個元素,每個P佔x個字節,每個Q佔y個字節。)
       怎麼判斷一個解是否可行呢?想想如果Q連續存儲,至少也得消耗n*y個字節,一個AB方案,如果算出的字節小於該值就是不可行的。否則這個內存越小,解就越優,依據這個來更新最優解ansA,ansB,ansN。


看着好複雜的題目。看了半天不懂,看網上人家的解釋也是一頭霧水。於是花了好久把他翻譯並理解。翻譯只是大概翻翻,但和原意基本一致,括號內的內容是我最終理解的內容。
翻譯如下:
Tuple博士正在給ACM公司研究數據挖掘。其中一個子工程是關於P、Q兩個數組的,兩個數組各含N組數據(這個“組”的單位是record,不知道怎麼翻譯好。論好好學習英語的重要性。)。這N個record的數據從0到N-1命名。數組P包含含密鑰的類似哈希的結構。P用來給程序定位要用哪個record,且這個數據的對應的Q裏的record待會會從Q數組裏恢復出來(如紫書中文的理解就是知道P的第i個record的偏移量求Q第i個數據的偏移量)。
數組P中的每個record長度爲Sp字節,Q的每個record長Sq字節。Tuple醬需要最最高效地執行這個程序,因爲這是整個數據挖掘一直要用到的地方。但是坑爹的是Sp和Sq只在運行時知道其大小,沒法在編譯時進行優化(哦就跟定義數組必須要用常量來定義的原因類似)。
找到P數組的第i個record的偏移量(不懂偏移量是什麼,差不多就是指:比如一個數組a[10],那麼a[3]的地址在首地址的右邊地三個,偏移量就是(&a[3]-a) · sizeof(int),也就是3 · sizeof(int)。如果我沒猜錯應該就是了)的最直接的方法是用以下函數:
Pods(i) = Sp * I (1)
Q的也一樣:
Qods(i) = Sq * I (2)
但是!乘法比加減法慢多了(哦你好煩啊,直接說你最後用了什麼就行了啊)。Tuple醬拒絕使用乘法。那麼他怎麼做的?他保存了已經計算好的第i個record的偏移量Pofs(i),而不是保存這個下標(索引)i(哦就相當於一個數組a[10],你已經計算得你要的數據是a[2],現在別人要調用a[2],你給他 p=&a[2]比直接給他i=2讓他自己找到a{2}更快。是這個意思吧*)。於是現在當他需要計算第i個record旁邊的record的偏移量時,就用下面的方程:
Pofs(i + 1) = Pofs(i) + Sp
Pofs(i - 1) = Pofs(i) – Sp
好吧,原來就相當於我們遍歷數組時用指針*p先指向首地址,然後不停p++指向下一個數據一樣啊,比直接調用a[i++]快啊
當一個P的record不管是用上面兩個方法的哪個(用乘法掃描數組,及用其他數據的偏移量用加減法獲得),Tuple醬需要從對應Q數組把數據恢復進去。要獲取Q的record的偏移量Qofs(i)也要計算。當Pofs(i)已知時可以直接用(1)、(2)兩個函數求得Qofs(i)。
Qofs(i) = Pofs(i)/SP SQ (3)(就把(1)、(2)整合了一下*)
然後又來了。“Unfortunately”,這個函數不僅有乘法,甚至還有除法!“我不管我不管,乘除就是比加減慢!我就是不允許乘除的出現!” Tuple醬傲嬌地鼓起了腮幫。
最後Tuple醬研究得出,可以這麼求Qofs(i):
Qofs’(i) = (Pofs(i) + Pofs(i) < < A) > > B (4)
Qofs’(i)那個’不是求導的意思哦,我說一開始怎麼沒看懂。)(等一下讓我查一下位移運算和加法運算的優先級。。。好的+比<<優先級高??不對啊,那他要這個括號幹嘛?看了下別人的代碼,這裏題目錯了。應該是(Pofs(i) +((Pofs(i) < < A)) > > B。但是還是沒看懂怎麼從(3)推出這個式子的??!!)(好的現在看懂了,這是一個新方法,和(3)無關,不是從(3)推導出來的。這個方法犧牲了些沒用上的內存但是速度快了。詳見分析
A和B是無符號整形。這個函數跑的比(3)不知道快到哪裏去了。但是不管A、B選何值,它一般不能總是產生和(3)一樣的結果(喵喵喵???爲什麼不一樣?沒看懂。根據紫書:“爲了讓(4)成立,在P數組連續存儲的情況下Q可以不連續存儲”我去?哪裏說可以不連續存儲了。好吧現在看懂了,最下面一起講。)。但是如果你願意犧牲點內存,你還是可以用。
傳統的佈局裏,Q在內存裏需要N*Sq字節去儲存整個數組。Tuple發現總是可以找到一個K(K<=N*Sq)(題目反了吧??!!K>=N · Sq還差不多,看樣例輸入輸出也明顯是1119>=1024 · 1),如果分配K字節的內存給Q且小心地找A和B的值,(4)會給不重複的把存儲地址給數組Q的N個record。
你的目標是寫一個程序找出當(4)被用時最小的內存K去分配給Q,對應的A、B也要找出來。若多個A、B得到一樣的最小的K,那麼找出A最小的那組,還不行,那就找B最小的。
(這個CSDN-markdown編輯器好垃圾啊,爲什麼不能輸出多個空格?導致不能每段首行縮進兩個字??!!狂按空格也沒效果。。我按空格你也要管。。)


分析:好的那麼就是說 (比如如圖,P是上面那個,每個數據佔3bit,Q佔2bit)以P的某個數據的偏移量按照(4)快速求出Q對應數據的地址(算的快的結果是部分內存沒用上)。所以原來(3)、(4)不是等價的,是Tuple醬另外想的方法。我說怎麼死活看不懂、推不出來(4)這個公式。這裏寫圖片描述
所以簡言之,已知A和B,P的每一個元素的偏移量Pofs(其實就是第i個元素首地址位置)通過含A和B的公式4計算得出Qofs(即求出Q的第i個元素的位置)後,即知道了Q的各個元素的位置。現在要求找出Q最少佔用多少空間,即求Q的後一個元素的最後一個字節的地址與分配的第一個內存地址的距離即可。(本來我還想Q的各個元素的存儲位置不能重疊,要不要判斷,但是題目已經說了,用公式4保證不會出現存儲重疊)
那麼暴力枚舉A,B即可。而還有個問題,A,B沒說範圍。那麼因爲

1 ≤ N ≤ 2^20,1 ≤ SP ≤ 2^10,1 ≤ SQ ≤ 2^10

因爲Pofs最大爲(N-1)*Sp,所以這裏最大(2^20-1) · 2^10差不多2的30次方。而long long64位,也就30多位可以給你移的。而且參考了 http://blog.csdn.net/code4101/article/details/38540759 這篇博文,那麼30多次的枚舉就行了。那麼說到這裏,這題其實是個超簡單的題目。
/*
本來我一開始看題目時覺得這都什麼鬼,看不懂。
但是看到紫書上說“本題有一定實際意義,不過描述比較抽象。如果對本題興趣不大,可以先跳過。”於是瞬間來了興趣… (—。—) 哈哈我簡直是個受虐狂。


#include<bits/stdc++.h>
using namespace std;
unsigned long long n,x,y;
unsigned long long ansk,ansa,ansb;
int main(void)
{
    while(cin>>n>>x>>y)
    {
        unsigned long long posi=(n-1)*x;//p最後一個元素的偏移量
        ansk=n*y<<10;//設爲足夠大的一個數
        for(unsigned long long a=0;a<32;a++)
            for(unsigned long long b=0;b<32;b++)
        {
            unsigned long long temp=((posi+(posi<<a))>>b);
            temp+=y;
            if(temp>=n*y)
            {   if(temp<ansk)
                {
                    ansk=temp;
                    ansa=a;
                    ansb=b;
                }

            }
        }
        cout<<ansk<<" "<<ansa<<" "<<ansb<<endl;
    }
    return 0;
}

發佈了104 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章