摩天樓 DP優化

2021-06-16 13:47:26 字數 3179 閱讀 7900

有n(≤1300)棟摩天樓,從左往右排成一行。第i棟的高度是i個單位,並且每棟摩天樓都有顏色(有些摩天樓的顏色相同),將這些摩天樓排列,有多少種排列的方法,使得從左往右看去,看到不多不少剛好l棟摩天樓,答案mod 1000000009。

這裡定義一下「看到」,首先若某棟摩天樓左邊有比它高的,那就看不到。如果從左往右依次連續看到的兩棟(或以上)摩天樓顏色相同,會誤認為是一棟。

由於這個關係到次序問題,所以dp的順序可以按照從大到小的順序來,這裡只需要把讀入的順序倒過來(讀入是從小到大,下面的描述第i棟的高度是n - i + 1)。

記f(j,i)為能夠看到j棟摩天樓,並且放置了前i棟摩天樓後,第一棟看到的樓就是第i棟樓。求出f(j,i)需要列舉前一棟看到的樓是什麼,假設看到的是k這棟樓:

如果k和i的顏色是一樣的,那麼會誤當成一棟,同時k + 1至i - 1這些樓需要放在k的後面,因為它們都比k矮,那麼前一棟看到的就不是k了,那麼,k + 1至i - 1這些樓放置的方案數為(假設下標為1),k * (k + 1) * (k + 2) * … * (k + i - 2),若k + 2 等於 i,那麼就什麼都不用乘,因為i,k之間沒有其他樓

:f(j,i) += f(j,k) * k * (k + 1) * (k + 2) * … * (k + i - 2);

如果顏色不一樣,那麼就能多看到一棟樓:f(j,i) += f(j - 1,k) * k * (k + 1) * (k + 2) * … * (k + i - 2)。

時間複雜度:首先n^2種狀態,轉移o(n),總為o(n^3)。

我們需要再優化一下轉移,可以畫個**來尋找方法:

摩天樓1,3,6是同一種顏色,而1,3,5,6中沒有乙個的顏色和摩天樓7相同,它們的顏色也不一定相同。現在若需要求f(3,7)(藍色部分),它的值等於:

f(3, 7) = 

+ f

(2, 1) * 1 * 2 * 3 * 4 * 5

+ f

(3, 2) * 2 * 3 * 4 * 5

+

f(2, 3) * 3 * 4 * 5

+ f(3, 4) * 4 * 5 + 

f(2, 5) * 5

+ f(2, 6)

把它分成黃色,綠色,藍色三個部分求解。

再來看看它前乙個同種顏色的摩天樓(第4棟):

f(3, 4) =

+ f(2, 1) * 1 * 2 * 3

+ f(3, 2) * 2 * 3 + 

f(2, 3) * 3  

看到沒有,f(3, 7)劃線部分的值和f(3, 4)是一樣的。並且加粗部分是劃線部分的4 * 5倍,黃色部分的值就求出來了!至於i * (i + 1)* … * j,可以預處理。然後,f(3, 4) * 4 * 5可以單獨算(綠色部分)。接著,就剩下f(2, 5) * 5 + f(2, 6)。既然第4棟摩天樓是前乙個和7同顏色的,那麼,4 + 1至7 - 1的樓都不和7相同顏色,這就可以用類似於部分和的東西。 

由於公式編輯麻煩,這裡就不貼了,找找規律就知道了。

那麼g(2, 6) = 

+ f(2, 1) * 1 * 2 * 3 * 4 * 5

+ f(2, 2) * 2 * 3 * 4 * 5

+

f(2, 3) * 3 * 4 * 5

+ f(2, 4) * 4 * 5 + 

f(2, 5) * 5

+ f(2, 6)

g(2, 4) = 

+ f(2, 1) * 1 * 2 * 3 

+ f(2, 2) * 2 * 3  + 

f(2, 3) * 3  + 

f(2, 4)

那麼我們所需要求的f(2, 5) * 5 + f(2, 6)就等於g(2, 6)減去4 * 5倍的g(2, 4)(橙色部分)。

至於g如何求,這應該很簡單吧。g(j,i) = g(j,i-1) * i + f(j, i)。

這樣,我們就可以o(1)實現求解某個f(j, i)了。問題就到此結束了,時間複雜度就是o(n^2)。

我的**下標是從0開始的,寫的一般般:

#include #include #include using namespace std;

typedef long long ll;

const ll mod = 1000000009;

const int n = 1300, c = 2707;

char x[n];

int n, l, col[n]; // 第i棟樓的顏色編號

int pre[c]; // pre[col],前乙個顏色為col的摩天樓的標號

ll f[n][n], // 即所說的f陣列

fac[n][n], // fac(i, j) 用來儲存 i * (i + 1)* … * j

sum[n]; // 即所說的g陣列,這裡省去了一維

inline int ord(char a)

int main()

// 加個哨兵,方便輸出

col[n] = c - 1;

// 把摩天樓的順序調轉

for (int i = 0; i < n >> 1; i ++) swap(col[i], col[n - i - 1]);

for (int i = 0; i <= n + 1; i ++)

for (int j = 0; j <= n + 1; j ++) fac[i][j] = 1ll;

for (int i = 1; i <= n; i ++)

for (int j = 1; j <= l + 1; j ++)

pre[col[i]] = i;

sum[i] = (sum[i - 1] * i + f[j - 1][i]) % mod;

} }printf("%i64d\n", f[l + 1][n]);

return 0;

}

AOIP 2015 耶加達的摩天樓

qwq 其實就是個最短路啊,然後發現建的邊最多會有n2 n 2條 所以我們考慮用分塊的思想來優化建圖。pi sqrt n 暴力加入每一條邊,每次最多sqrt n 條邊。pi sqrt n 對於每個點新增sqrt n 個輔助點,這裡可以理解成一棟樓有許多層,每一層一步能走的範圍都不同,然後每一層分別連...

APIO 2015 耶加達的摩天樓

題目鏈結 演算法 考慮將每個 doge 向其所能到達的樓連邊 直接spfa求單源最短路可以獲得57分 那麼 怎樣拿到滿分呢?我們發現這張圖的邊的數量達到了nm的數量級 考慮分塊 將每個點拆成sqrt n 個點 將每個pi sqrt n 的點向 bi pi 連邊 這樣的邊不會超過n sqrt n 條 ...

APIO2015 耶加達的摩天樓

首先可以看出這是一道求最短路的題目,但暴力建圖最多n2 條邊,所以考慮建圖優化 對於p sqrt n 的青蛙可以直接暴力建,最多n sqrt n 條邊 對於p1 define maxn 4000010ul 2 include 3 include 4 include 5 include 6 7usin...