題目建立上乙個作業的題目基礎上,上一次作業是要求在乙個一維序列裡找乙個最大連續子串,這次task最基礎的要求是在乙個二維表裡找乙個最大連續子矩形,但是這次作業有若干個公升級版,分別要求可以加入執行引數[\h][\v][\a],其中\h選項代表給定的二維表是水平迴圈的,\v代表給定的二維表是水平迴圈的,\a表示結果可以突破矩形的限制,尋找乙個最大的連通塊,而非矩形。
這次題目最難的一部分是實現/a的操作,相比之下其他要求實現比較簡單,先從簡單的說起。
從二維表裡找乙個最大的子矩形,這和一維的情況非常類似,這促使我們思考如何將這種情況轉化到一維的情況,我們觀察二維表中的任意乙個子矩形。
我們可以發現子矩形只是選中了一些連續的子列,每個子列的始終位置都是相同的,而且每個子列的值都不會影響其他子列的值,這樣我們把子列的值得的和作為乙個元素,就得到了乙個一維序列。
這時就可以使用在作業毅力的演算法來處理這種狀態,考慮到要遍歷到所有情況,我們僅需要枚舉行上每個行之間的區間,時間複雜度為o(n^2),然後再用homework-01裡方法處理兩行之間的資料即可,時間複雜度為o(m),總時間複雜度為o(n^2m),這樣這道題目就比較好的解決了。不過我們並不能確定這個問題時間複雜度下界就是o(n^2m),找出乙個o(nm)的演算法還是非常有吸引力的,畢竟這和一維的情況存在更多的一致性。
有了上面的基礎我們再考慮/v和/h的情況,這兩種情況實際上是等價的,因此我們僅考慮/h的情況,/h選項表示二維表在水平方向上時迴圈連通的,我採用了在水平方向上擴充套件一次二維表的方法,擴充套件如下圖
擴充套件之後我們在n*m的矩陣的基礎上得到了乙個n*2m的矩陣上進行同樣的演算法,這時還需要加乙個限制,考察一行發現如果覆蓋整個一行的列長超過m的話會在當前解中加入重複的元素,我們此時列舉列的起始位置,同時保證列長為m即可,這樣的時間複雜度為o(n^2m^2),還是乙個非常理想的時間複雜度。
最後我們來討論一下/a選項,它要求找乙個任意的最大聯通塊即可,這個問題難度很高(因為我想了好幾天都沒想到乙個好的解法),題目要求的矩陣最大規模為32*32,初看這個規模顯得非常小,但是對於這道題目我們難以找到乙個多項式的解,今天在課上也確認了這確實是乙個np問題,解決這個問題有很多思路,這裡我們簡單討論幾種思路,從經驗上我們可以發現如果乙個非負的格仔在最終解裡,那麼它相鄰的非負格仔也一定在最終解裡,這樣考慮的話就把我們生成矩陣中的所有非負連通塊,最終解一定是某些塊的集合加入它們之間相連的負的格仔,我們列舉這樣的集合,然後判斷集合裡的塊是否能相連,具體方法為從乙個塊中向外遍歷負格仔,如果遍歷的路徑和的絕對值已經比該塊大了,就停止遍歷,可以使用記憶化來優化一下,如果能遍歷的目標塊就找一條最小的(同時要看這條路的絕對值和是否也比目標塊小),這時把這兩塊連成一塊,繼續連線其他塊,考慮每一種情況後取乙個最優值,視為找到的乙個解,這個方案的優點是能較快的找到乙個好的近似解,缺點是**複雜,難以實現,而且它目前僅能求乙個近似解,並不能證明它是最優的,不滿足題目要求。再考慮另一種方法,狀態壓縮動態規劃,狀壓dp將每一行壓為乙個狀態,這個狀態用乙個r進製數表示,由於本題要求找到的塊具有連通性,那麼我們需要乙個m進製的數來維護連通性,但本題目有一定特殊性,如果把它視作插頭dp的話這是乙個4插頭dp也就是說如果相鄰兩個格仔都被選中的話那麼他們一定聯通的(感謝tony shaw的指導),這樣考慮的話僅需m/2進製就可以滿足需求,我們僅需維護我們當前狀態裡沒有不與當前行連通的塊,並且用每乙個只有乙個塊的狀態去更新最終解,這樣就能很好地解決問題。這個方案的優點是思路清晰,**相對好寫(實際上實現也相當困難),確定是時間複雜度高,我們假定矩陣的規模滿足m < n,那麼我們的複雜度為o(n(m/2)^(2m)),利用輪廓線優化可以得到o(nm(m/2)^m)的複雜度,題目限制是(n,m)<=(32,32),最壞情況下該演算法的規模為32*32*16^32,這個複雜度實在難以接受,但是如果我們考慮一種特例——矩陣的乙個維度特別小,那麼這種方法就有很大的優勢。
經過一晚上的奮鬥成功在vs2012下完成了performance analyze 和 unit test,第一次做這個非常吃力,但是還是頗有收穫,先看看unit test。
寫了三個測試方法,分別測試了三個選項以及基礎情況的輸出,使用了三個不同的測試樣例,這次為了能更快進行一些初步的unit test實戰,沒有對資料進行容錯處理,所以樣例規模都很小,其中testacondition我只是採用了找乙個最大非負連通子塊的方法來代替,所以最後的結果只是乙個近似值,如果在assert巨集裡加入容限就能通過test。再看看測試的覆蓋率。
這個工程我寫了一部分函式來模組化,從上表看**有效性還是很高的,但我初學單元測試,還不是很好的看懂覆蓋圖。
完成第一次單元測試後,個人感覺單元測試這個工具非常有用,但是需要很大的精力維護,它可以很好的維護**的正確性,單元測試可能非常依賴於測試樣例,管理好測試用例對於單元測試是乙個重要的組成部分,編寫單元測試可能還要遵循簡短的方法,我在寫單元測試時已經有了乙個單元測試的單元測試,但我認為這樣的代價是不值得的,好的單元測試應該邏輯清晰而簡潔,具有非常好的可讀性,以便於維護。
這次單元測試我但我耽誤時間最多的是配置過程,這裡有乙個小細節,使用vs2012單元測試時原工程一定要匯出乙個dll作為symbol才能正常連線,不過這短時間也讓我熟悉了下vs中solution-projects的檔案結構,感覺這個結構非常舒服,可以加入很多任務具配合在一起使用同一管理,很讚一點設計。
最後看一下performance analyze
performance analyze做起來就太傻瓜化了,一鍵完成,不過還是學到了新工具,效能分析我使用了乙個500*500的大矩陣,矩陣的元素為16位整型範圍內,從上圖可以看到主要的運算集中在演算法模組,我在演算法實現上確實犧牲一部分時間系能,程式還能採用空間換時間的方法繼續優化,不過都是只是常數上的,而且會破壞**結構,於是沒有進一步優化。
02 最大子矩陣
描述 已知矩陣的大小定義為矩陣中所有元素的和。給定乙個矩陣,你的任務是找到最大的非空 大小至少是1 1 子矩陣。比如,如下4 4的矩陣 0 2 7 0 9 2 6 2 4 1 4 1 1 8 0 2 的最大子矩陣是 9 2 4 1 1 8 這個子矩陣的大小是15。輸入輸入是乙個n n的矩陣。輸入的第...
最大子陣列和02
1 題目要求 1 輸入乙個整形陣列,陣列裡有正數也有負數 2 陣列中連續的乙個或多個整數組成乙個子陣列,每個子陣列都有乙個和 3 如果陣列a 0 a j 1 首位相鄰,允許a i 1 a n 1 a 0 a i 1 之和最大 4 同時返回最大子陣列的位置。2 實現思路 1 先要輸入一組整形數,直到輸...
最大子區間和 打水漂 rqnoj145
輸入檔案包含n 1個整數,第一行為乙個整數n n 10000 從第二行工n個數,第i個整數表示第i條魚的美觀值範 圍為 500.500 當所有整數都為負數時輸出0.輸出檔案包含兩行,第一行為石子的起點和落點,用空格隔開.第二行為乙個整數表示所得到的兩條魚之間美觀值總和.6 2 11 4 13 5 2...