bzoj 4321 queue2 - 動態規劃
Input
只有一行且為用空格隔開的一個正整數 N,其中 100%的數據滿足 1≤N ≤ 1000;Output
一個非負整數,表示方案數對 7777777 取模。Sample Input
4
Sample Output
2 樣例解釋:有兩種方案 2 4 1 3 和 3 1 4 2
題目大意 問n個數的排列且滿足任意兩個相鄰的數的差都大於1的個數。
顯然dp。(難道還能爆搜?)
現在來設計狀態和轉移。因為狀態和轉移都不好想,所以根據常用套路猜測一下。
1)f[i][j]表示已經確定了前i個數,最後一個數是j的合法方案數。看似很棒,然而你知道可行字符集嗎?當然不知道。那它能存下來嗎?當然不能(要是能存下來,和爆搜有區別嗎?)因此,只能另外思考。
2)f[i]表示n個位置中已經填完1 - i這幾個數。然而發現。。轉移的話同樣需要很多東西。。果斷放棄
3)考慮將排列當成一個動態數組,f[i]表示當n = i時的答案。
考慮轉移。然而總感覺少了些什麽。。腫麽辦?加狀態!
於是便有了f[i][j]表示已經填了1 ~ i,其中包含j個不滿足條件的對子。
似乎轉移輕松了些。然而很快又發現了不對勁。考慮將(i + 1)插入排列,例如當i = 3時,排列 1 3 2變成排列 1 4 3 2 和排列 1 3 4 2 的時候增加的不滿足條件的對子有點不一樣,情況也不同於排列 3 1 2 將4加在3兩端。
所以根據觀察可以得出應該增加的狀態應該是i是否和(i - 1)相鄰。
現在開始討論幾種情況。(下面討論的轉移請自行取模)
//假設讀者的小學數學沒有問題
i.對於f[i][j][0]的轉移
a.首先是將(i + 1)放在i的兩端,這樣會導致增加1個不合法的對子。故有這麽一個轉移: f[i + 1][j + 1
b.然後是將(i + 1)放在其他地方(這樣的一個地方有(i + 1 - j - 2)個,即(i - j - 1)個),使得這樣什麽都不會發生。所以就有這麽一個轉移: f[i + 1][j][0] += f[i][j][0] * 1LL * (i - j - 1)
c.最後是將(i + 1)拿去破壞已經有的對子的相對穩定結構(我應該是化學學多了),比如在 2 3 中強行插入一個4,這樣就有個轉移: f[i + 1][j - 1][0] += f[i][j][0] * 1LL * j
ii.對於f[i][j][1]的轉移
a.首先是將(i + 1)放在i和(i - 1)的兩端,這樣會增加對子i和(i + 1),並且會減少1個對子,故有轉移 f[i + 1][j][1] += f[i][j][1]
b.考慮將(i + 1)扔在上一種情況提到的一側的另一側,這樣只會增加一個對子,故有轉移 f[i + 1][j + 1][1] += f[i][j][1]
c.然後考慮將(i + 1)放在不會有影響的地方,這樣的地方會有(i + 1 - j - 1)個,即(i - j)個,相信有人一定很好奇為什麽情況i.b是(i - j - 1),因為這個地方的j中包含了i的一端,而上面並沒有包含(如果一臉霧水請畫圖吧,個人認為比較顯然)。所以有轉移 f[i + 1][j][0] += f[i][j][1] * 1LL * (i - j) 。
d.最後仍然是把(i + 1)拿去搞事情。。由於只能拆散對子,所以不能與i相鄰,所以只能有(j - 1)個位置可選擇。因此有轉移 f[i + 1][j - 1][0] += f[i][j][1] * 1ll * (j - 1)
然後整理成程序就好了。
//Q:題目中的"沙茶"是什麽鬼 //A:當你對你的父母師長大聲說出這個詞之後你就會有深刻影響了!
Code
1 /** 2 * bzoj 3 * Problem#4321 4 * Accepted 5 * Time: 272ms 6 * Memory: 9132k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 #ifdef WIN32 11 #define Auto "%I64d" 12 #else 13 #define Auto "%lld" 14 #endif 15 #define ll long long 16 17 const int N = 1002, M = 7777777; 18 19 int n; 20 int f[N][N][2]; 21 22 template<typename T> 23 inline void mod_plus(int& a, T b) { 24 a = (a + b) % M; 25 } 26 27 inline void init() { 28 scanf("%d", &n); 29 } 30 31 inline void solve() { 32 f[1][0][0] = 1; 33 for(int i = 1; i < n; i++) { 34 for(int j = 0; j < i; j++) { 35 mod_plus(f[i + 1][j + 1][1], f[i][j][0] * 2 + f[i][j][1]); 36 mod_plus(f[i + 1][j][1], f[i][j][1]); 37 mod_plus(f[i + 1][j][0], f[i][j][0] * 1LL * (i - j - 1) + f[i][j][1] * 1LL * (i - j)); 38 if(j) mod_plus(f[i + 1][j - 1][0], f[i][j][1] * 1ll * (j - 1) + f[i][j][0] * 1LL * j); 39 } 40 } 41 printf(Auto, f[n][0][0]); 42 } 43 44 int main() { 45 init(); 46 solve(); 47 return 0; 48 }
bzoj 4321 queue2 - 動態規劃