中科院卜東波算法課第三題作業(dp)oj總結

說在之前我真的菜,乃至於明顯很簡單的兩道題我寫了15個小時左右,我是真的菜,而且我現在很生自己的氣。正常人自己手打代碼估計是20分鐘+20分鐘,一個小時就能搞定。爲什麼花這麼久的時間,因爲我“探索”了很久,現將問題總結,並期待下一次不要遇到相同的問題。(下面兩)

1、lcs最大公共子序列

卜老師介紹了一種算法

我沒有拿正常打dp表的方式來做這道題,而採取了老師講述的那種方法,這就是噩夢的來源。 

1、僞代碼第二行的理解:

  1. matches(k+1)=(m+1,n+1)

    有k個匹配爲什麼要搞一個k+1,於是我寫代碼的時候華麗的忽視了這一句,導致錯誤。我們需要注意到,matches的添加方式和之後的讀取方式,對!一行一行順序讀取,那麼我們假設越到最後opt越大(由於每一次都取之前表裏的最大值)肯定是遞增的,那麼最後輸出的結果就是opt[l1][l2]。假如bdad和abd那麼最後一對是處在3的a和處在1的a,那麼最後一個得到的opt的值是1。所以要加一個k+1行的,即統計上面所有的最大值,最後輸出的時候k-1即可。

    for(int i = 0;i < l1; i++){
        for(int j = 0;j < l2; j++){
            if (S[i] == T[j]){
                matches[matches_index] = make_pair(i,j);
                matches_index++;
            }
        }
    }
    for(int k = 0; k<= matches_index; k++){
        int i = matches[k].first;  
        int j = matches[k].second;
        opt[k] = 1;
        if (k >= 1){
            for(int l = 0; l < k; l++){
                //找到當前字符對 之前的所有字符對 中 opt值最大的一個值 進行+1操作
                int i_ = matches[l].first;  
                int j_ = matches[l].second; 
                if (i_<i && j_<j){
                    opt[k] = max(opt[k],opt[l]+1);
                } 
            }
        }

    }

2、STL和pair的運用,struct和class不太熟悉,make_pair()什麼的都要熟悉,在嘗試使用什麼表達方式的時候糾結了很久,其實我覺得!!!寫算法題,沒有必要這麼糾結,因小失大,重點是算法思想,而不是一些很虛虛的東西。

3、日常關於ijk的起點和終點的巨大懷疑和糾結,啊,應該是和理解有很大的關係。

4、提交上去,70%的樣例全部runtime error。太苦了。感謝我的尼老師(在自己有一堆作業的情況下還幫我看了代碼分析了算法),在他幫助下,我認識到了我代碼的不足和算法的性能和改進方法。

  • 定義數組的時候這不是一個好習慣
        int l1,l2;
        cin>>l1>>l2;
        char S[l1],T[l2];
  • Const int MAX_N = 1000改成1005 防止越過邊界
  • 開1000^1000的數組最好開成全局數組

5、同時也在尼老師的幫助下我們開始分析這個算法,

它這個算法時間是有問題的,你考慮一下。假如是aaa和aaa,匹配的matches是9,對吧?也就是l^2。然後代碼22行要遍歷 l^2,然後第27行又需要一個 l^2,這就是 l^4的複雜度了。。

我看了看他課件,好像就是把所有的matching points找出來了。這樣的話肯定會超時

所以最耽誤時間的兩步是建立matches表和查找每一步的matches[i]的之前的matches裏最大的值

我提出了

新開一張表記錄一下所有ij點之前最大的i_ j_是多少

後來發現不用新開表,就在原來的opt表上

s[i]==t[j],就左邊或上邊的值+1  -----> s[i]==t[j],就左上方的值+1 

所以!!!!問題來了!!

改進這個算法 兜了一圈 回到了dp最原始的做法!哈哈哈哈

2、多維01揹包

就是揹包除了體積和價值還有重量,好吧我上來就被嚇懵了。還在網上找了很久算法,找不到我還找朋友吐槽來着。其實就是多加一個維度而已,真的是而已,太簡單了實際上。就是多加了一個維度,什麼都沒改。

dp[j][k] = max(dp[j][k],dp[j - disk_spaces[i]][k - memorys[i]] + users[i]);

遇到的問題是:

1、被嚇懵了,對。

2、又是關於該死的ijk問題啊啊啊啊,jk不需要減1,是幾就是幾,於是引發了一個問題,什麼是從0開始,什麼是從1。序號這種應該是從0開始,但是像如果那個下角標就代替一個實際的數的話,從0開始就是很傻的行爲。所以這裏i從0,jk從1開始。

3、i是序號從底層開始算,從0往上。但是jk是從大往小裏寫【有趣的是,我在寫這一篇博客的時候突然開始想爲什麼原來只有體積的時候都可以從小往上寫,現在多維就不行了,哈哈哈哈,還有我就思考出了一個重大發現】【這裏先留個彩蛋問題,之後揭曉,這其實還有個優化存在】

4、鼓起勇氣過oj了,結果全是runtime error和這個肝上了好生氣。因爲這些取值都是1000,也就是說我的opt數組得開1000*1000*1000。根本過不去。

5、感謝我的刁爸爸給我提供了代碼,我研究得知一種降維度的辦法,就是共享二維的表格,而不是開三維數組,直接節省了1000倍的空間。也就是說直接把i去掉。【彩蛋答案】因爲共享了二維表,相當於算第i層的時候需要取第i-1層的數據的時候,不是多開一維由原來在空間裏的開闢的第i層數據來表示,而是放在二維表裏。如果ij是從0開始往大寫,則算jk時候更新二維表的時候會覆蓋原有i-1層得出的值的表格,導致用的數據是第i層新算出的,而不是原來i-1層保留的數據。如果倒着算,倒着填表,使用之前的數據就是第i-1層的

 

整個思路就是這樣的。感謝在oj路上幫助我的朋友們。

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