跳到主要內容

UVa 103 Stacking Boxes

題目講解

這題給你一堆多維的箱子,要找出最長的裝箱序列,這個最常裝箱序列就是像俄羅斯娃娃那樣,大箱子裡面放入小箱子,一層包一層,必須要可以裝最多層這樣。

這題的麻煩就是各個箱子之間不一定有的大小關係 (數學上打這叫偏序集 Partial Order Set ,不過這兒我們先不管 ),比方說有兩個三維箱子 BoxA(1,4,5) 、BoxB(2,4,6) ,B箱比較大,所以 BoxA 可以裝進 BoxB 裡,那麼我們說 BoxA < BoxB 。

但來看 BoxC(1,5,6) 跟 BoxD (2,4,5) 呢? 我們既不能說BoxC < BoxD ,也不能說 BoxC > BoxD,BoxC 與 BoxD 兩者之間就沒有大小關係。

一個簡單的表示法就是有向圖(Directed Graph)
比如說 A(1,2,3) B(2,4,6) C(2,3,7) D(50,50,50) E(10,10,100) 的有向圖表示法:



到此為止,本題的最長裝箱序列,已經有兩種解法,第一個方法就是找圖的最長路徑,稍微修改 Warsahll algorithm 來解,第二個方法是跑 LIS 。

我這裡用 LIS(最長上昇子串列) 解法。

//先定義一個box struct
struct Box {
    int no;
    int edge[10];
};

Box box[30]; 

LIS 法的關鍵是要先對箱子排序,確保小箱子一定在大箱子前,至於沒有大小關係箱子的就隨便放沒關係。這個排序以圖的角度是拓樸排序,以字串的角度是字典排序,兩者都行。

sort(box[i].edge) for all i  //先對箱子內部的邊長排序

int lexico_less(const Box& a, const Box& b) {
    for(int i=0;i<dimension;++i)
        if( !(a.edge[i]<=b.edge[i]) )
            return false;
    return true;
}
sort(box) by lexico_less(); //再用字典序排箱子
一切都準備好後,就可以直接套用LIS解了。
// LIS
int length[MAX_BOX];
int   prev[MAX_BOX];

length[i] = 1  for all i;
  prev[i] = -1 for all i;

for(int i=0;i<num_box;++i)
    for(int j=i+1;j<num_box;++j) {
        if( box[i]<box[j] ) {
            if(length[i]+1 > length[j] ){
                length[j] = length[i]+1;
                prev[j] = i;
            }
        }
    }
輸出結果
int max = max_element(length, length+num_box) - length;
    //print the result
    printf("%d\n", length[max]);

    int output_seq[MAX_BOX];
    int size = 0;

    int cur = max;
    do {
        output_seq[size++] = box[cur].no;
        cur = prev[cur];
    } while( cur!= -1 );

    for(int i=size-1;i>0;--i)
        printf("%d ", output_seq[i]);
    printf("%d\n", output_seq[0]);
}

碎碎念

值得慶賀的事,Ranking 進世界百大!!!! 我相信這是誤打誤撞XDDD,不過真的蠻爽的。 @2008.11.10

留言

  1. 好像蠻多DP題都可以化成圖來做
    不過這題我還是用LIS
    不過時間複雜度很難算倒是真的
    上網找解答的時候
    才發現有人的寫法跟我一樣奇怪XDD

    回覆刪除
  2. 小弟正在寫acm 103題,但是卡了一陣子,實在想不到什麼好方法寫,是不是可以指點一下你解題的概念,雖然有程式碼,但是我對你使用的語言不很熟,謝謝

    回覆刪除
  3. 我想千言萬語不如一個好的方向
    試著去搜尋 LIS算法(最長上升子序列)

    這題的程式碼在下我自己都看得很心虛呀,寫得亂七八糟

    回覆刪除
  4. 其實這題不用DP

    用greedy也可以AC

    回覆刪除
  5. 用greedy也能AC是因為測資薄弱,不夠有鑑別力,其實greedy作法是有缺陷的。

    回覆刪除
  6. 感謝題解~有向圖的思維沒想到
    請問一下
    int lexico_less(const Box& a, const Box& b)
    這個排出來是照可不可以放入吧?
    字典排序不是a.edge[i]<b.edge[i])

    回覆刪除

張貼留言

這個網誌中的熱門文章

讀書心得: 你以為你以為的就是你以為的嗎?

當初想要接觸哲學的時候,挑上的第一本書就是「你以為你以為就是你以為的嗎?」,因為書名太好玩了。讀完後發現內容也一樣好玩,整本書的形式有點像坊間流行的心理測驗小書,每章的開頭都要讀者先做一組題目,後面接著就對你的答案做一番分析。 我自己的讀後感是,恩,這些題目對一個以前從未接觸過哲學的人來說太過犀利,第一次作答的時候,每翻過一頁都好像在呼自己巴掌,臉頰很燙。 「你以為你以為就是你以為的嗎?」書名念起來很拗口,但是很貼切,因為這些題目為的是要檢查我們腦袋內的想法是否一致,在邏輯上有沒有BUG。 我舉個例子,書裡有道題目「只要不傷害他人,任何人都有權自由追求自己的目標」要讀者回答同不同意,我認為這句話聽起來相當合理,所以勾了同意。過了幾題後,出現另一道題目是「為個人吸食而持有毒品的行為應予除罪化」,這次我的直覺是毒品這麼危險,怎麼可以除罪化呢,馬上勾不同意。 但是,我沒有發現這是刻意安排的陷阱,因為這兩句話其實講的是同一回事。 單純個人持有毒品,不散佈也不販賣,就不算傷害他人,那他就應該有自由追求自己的「吸食毒品」目標的權利,畢竟他只有傷害自己呀。這敘述聽起來有點危險,不過我必須承認一開始的確想的不夠清楚,我以為第一句話是真理,但馬上被反例打了自己的臉。 再舉一個例子,首先是「對藝術品的評斷,純粹是個人品味的問題」,接著是「米開朗基羅是史上數一數二的偉大藝術家」,這牽涉到評斷藝術的標準,不過你只能認同兩句話的其中之一。 書中我最有興趣的是「神明DIY工作室」與「信仰殺戮戰場」,這兩章擺明了直衝基督徒而來。當中有些問題圍繞著以下的敘述,如果你同意「神是全知、全能、又全然慈愛」,那該怎麼解釋世界上發生的許多苦難呢? 比如說被南亞海嘯淹沒的小女孩? 如果神沒辦法消除這些苦難,祂就不是全能。如果神沒辦法事先知道創造出來的世界會有這些苦難,祂就不是全知。如果神明知道有苦難,也有能力去掉,但是卻故意不做,那祂就不是全然慈愛。 我思考後的結論是,全然慈愛的神並不等於神希望世上的苦難越少越好,這些苦難都是在祂的允許下發生的。 書裡指出了一個基督徒的通病,被問倒了之後就嚷嚷「你不知道神是超越人所理解的嗎 」,但回頭又馬上賦予神非常明確地人的屬性。後來我也理解到這些尖銳的問題並不是故意要為難我對神的看法,而是逼迫我去反思一些比較深層的宗教議題,就像我...

UVa 10125 Sumsets

解題策略 這題的解法很直接,要找d=a+b+c 用四層迴圈下去跑a,b,c,d就好了 XDDDD 我犯了幾個錯誤 要找最大符合d (意思是數列中可能出現好幾個符合要求的解),所以迴圈應該由最大元素往下找,第一個找到的解就是答案,我一開始由最小元素往上遞增尋找,拿了WA。 沒找到解就回傳0,殊不知0也有可能是解: 0 = -5 + 3 + 2,這裡也吃了一個WA,所以我後來改回傳 INT_MAX 作為無解。 這題有負值,所以 -5 = -10 + -2 + 7 ,這樣算一組合法的解。 是比較需要小心的地方。 官網論壇上的(a+b)=(d-c)法,方法複雜很多,卻沒有比較快。