我們先來看一看這個問題的簡化版本:只用1×
2 1×2
和2×1 2×1
兩種方塊覆蓋m×
n m×n
的平面。
首先,狀態壓縮是毋庸置疑的。若某個方塊被覆蓋則為1,沒有被覆蓋則為0。這樣,每一行的狀態可以用乙個二進位制數來表示,且其轉化為十進位制的大小不超過29
=512
2 9=
512。
為了下文擴充套件到當前問題,這裡用遞迴來描述解法。由於每個方塊最多影響兩行,遞迴時需要的行引數只有當前行和上一行。但是,遞迴是對列從左往右進行的。具體思路我們在**中說明。
//設m是總行數,n是總列數
void dp(int row, int col, int now, int last)
dp(row, col + 1, (now << 1) | 1, last << 1); //放2*1的方塊
/*以這個狀態轉移為例解釋一下引數變化的含義。當前行不變,還是row;2*1的方塊只佔一列,因此col+1;放過之後,now的這一列是被覆蓋的,因此把now右端新增1;能放2*1的方塊要求上一行的這個位置本來沒有被覆蓋,因此把last右端新增0。*/
dp(row, col + 2, (now << 2) | 3, (last << 2) | 3); //放1*2的方塊
/*如果放1*2的方塊但上一行對應的位置沒有被覆蓋的話,以後就沒有機會再去覆蓋了,不符合題意。因此last右端新增兩個1。*/
dp(row, col + 1, now << 1, (last << 1) | 1); //不放。同上,上一行的這個位置必須已被覆蓋。
}
邊界條件:f[0][i] = 0
,f[0][(1 << n) - 1] = 1
。表示第0行事先視為全部鋪滿,若搜尋第一行時出現2*1的方塊越界的情況,last(代表第0行)中會出現0,最後進行累加時必然是加0。最後答案是f[m][(1 << n) - 1]
。
以上做法的關鍵在於理解:我們是在鋪當前行,上一行要正好留出空缺。
有了上面的基礎,我們再來考慮如何處理要求的變化:缺一角的2×
2 2×2
方塊。經過多次嘗試,我發現不能再要求當前行和上一行始終處於同一列;但是,列數差也不能超過
2 2
。因此,我多加兩個只能取
0' role="presentation" style="position: relative;">00和
1 1
遞迴引數exnow
和exlast
。exnow=1
表示當前行比上一行多一列,exlast=0
表示上一行比當前行多一列。遞迴的思路沒有什麼變化,不過情況變得很複雜,還要小心重複。具體分類在**中說明。
void dp(int row, int col, int now, int last, int exnow, int exlast)
if (exnow == 0 && exlast == 0)
else
if (exnow == 1 && exlast == 0)
else
}
讀者可以思考一下,為什麼沒有這幾種情況:
1、不放,exnow = 1
或exlast = 1
;
2、exnow = 1
時,缺口不放,col + 1
,exnow = 0
;
3、exlast = 1
時,缺口視為已放,col + 1
,exlast = 0
。
當然,如果把以上情況新增進去,就必須把另一些情況去除來避免重複。
這裡把小資料的答案給出,供除錯用。mn
ans235
24112
52426
53338
34553
5140
題解 P1092 蟲食算
聽說正解是高斯消元吶,但是我不會 看到大家都寫了搜尋。一種實現很簡單的方法是列舉1 n的排列,判斷是否可行。我算了算時間複雜度 其實我不會算,就大概估計了一下 發現會超時。由於不會算複雜度,我對於這樣的暴搜能過50表示驚訝 o 如果按照豎式從右至左的順序搜,就可以邊搜邊判斷是否可行了。我寫得很麻煩,...
CodeVS沖杯之路 P1092
不充錢,你怎麼ac?題目 嗯,這道題有一定難度啊,需要先用擴充套件歐幾里得演算法求出逆元,然後按照大小構一顆帶邊權為小時數的樹 樹鏈剖分後在樹上dp,設f i j 為以 i 為根 j 為子樹的最小的那一天 注意dp方程是有單調性的,可以用動態仙人掌維護,最後答案容斥一下即可 目測 量8k 1 inc...
洛谷p1092合唱隊形
原題 如果有人不知道最長上公升子串行請先去看它。題意就是要找中間高,兩邊矮的隊伍,因為可以踢人,所以所求隊形不一定在原序列中連續 就聯想到最長上公升子串行,從前面求最長上公升子串行g 從後面求最長上公升子串行f 每乙個值的兩方子串行都求出來了,根據題意,找到乙個最大的點max f g include...