二叉搜尋樹 區間DP

2022-03-24 18:43:54 字數 2338 閱讀 3885

有\(n\)個結點,第\(i\)個結點的權值為\(i\)。

你需要對它們進行一些操作並維護一些資訊,因此,你需要對它們建立一棵二叉搜尋樹。在整個操作過程中,第\(i\)個點需要被操作 \(x_i\) 次,每次你需要從根結點一路走到第 \(i\) 個點,耗時為經過的結點數。最小化你的總耗時。

第一行乙個整數n,第二行n個整數x1~xn。

輸出格式

一行乙個整數表示答案。

5

8 2 1 4 3

35
對於10%的資料,\(n \leq 10\)。

對於40%的資料,\(n \leq 300\)。

對於70%的資料,\(n \leq 2000\)。

對於100%的資料,\(n \leq 5000\),\(1 \leq x_i \leq 10^9\) 。

不要被題目給嚇到,這題並不用bst。

因為區間內的節點的權值是單增且連續的。

所以我們考慮從區間內單獨拎某乙個節點出來作為根,他左邊的點一定都會是他的左子樹,右邊的一定會是他的右子樹。

從這裡直接想到區間dp可能有點難,所以我們可以先從記憶化dfs的方向來考慮。

我們列舉根 \(k\),把整個區間分成左右兩半,相當於是把 \(k\) 的左邊和右邊的節點的深度都增加了1,對於答案來說,答案會增加 \(sum[k - 1] - sum[l - 1] + sum[r] - sum[k + 1] + x[k]\),最後的 \(x[k]\) 是根自己。(\(sum\)為 \(x_i\)字首和)

化簡一下就是 \(sum[r] - sum[l - 1]\),是不是很清新。

然後我們就可以愉快地將乙個大問題分成兩個子問題,再繼續愉快地dfs,看起來沒毛病對不對。

然而問題是在dfs的過程中我們並不能知道前面的斷點依次是多少,深度也就無從得知,自然回溯的時候會出問題。如果記錄一下的話就成了純粹的爆搜。

大概是這樣解釋的,具體細節咱也解釋不太清 (畢竟考場上我連爆搜都沒想到)

所以該怎麼辦呢,考慮一下:區間、斷點,一定會有神犇 (因為為同機房就有乙個) 能聯想到區間dp的四邊形不等式優化...於是這題就可以用區間dp來做了。

定義一下 \(f[l][r]\) 為 \(l\) 到 \(r\) 的區間的最小答案。

\(g[l][r]\) 為 區間 \([l,r]\) 的最優斷點,不理解請自行學習四邊形不等式優化。(只是這個人太菜不會講而已)

於是結合上面關於dfs的思考,有了轉移方程: \(f[l][r] = min(f[l][r], f[l][k - 1] + f[k + 1][r] + sum[r] - sum[l-1])\)

其中 \(k\) 為我們列舉的斷點,根據四邊形不等式可以判斷最優點一定在 \(g[l][r-1]\) 到 \(g[l+1][r]\) 之間。

然後我們就可以愉快地dp了。

然後當你愉快地打出區間dp的板子,發現t飛了。

再然後,這是為什麼呢。因為我們一般的區間dp都是第一維列舉長度,第二維列舉左端點 \(l\),這樣一般是沒問題的,複雜度也是穩穩的 \(n^2\),但他就是t了。

這涉及到二維陣列的儲存和隨機訪問的效率問題,感性理解一下就是二維陣列在記憶體裡是一行一行來儲存的,如果我們先列舉長度再列舉端點,那麼每相鄰兩次的 \(l\) 跟 \(l\) 、\(r\) 跟 \(r\) 是不連續的,所以在列舉時就會在一行行記憶體裡跳來跳去,用屁股想也知道這樣會慢。

但是一般只要演算法的瓶頸複雜度足夠了,這樣小常數不算什麼。然而,有一種生物叫做毒瘤出題人...

所以就有了下面**中的寫法,\(l\) 倒序列舉, \(r\) 正序列舉,既可以保證正確性 (正確性應該不需要我證吧...),又可以保證在訪問記憶體時 \(l\) 固定,\(r\) 也是連續的,這樣就只會在一行當中訪問,自然會快一些。

當然別的區間dp也是可以這麼寫的。

#include using namespace std;

const int maxn = 5005;

char buf[1 << 20], *p1 = buf, *p2 = buf;

char getc()

return *p1++;

}int read()

while(c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();

return s * w;

}int n;

long long sum[maxn], f[maxn][maxn];

int g[maxn][maxn];

int main()

}} }

printf("%lld\n", f[1][n]);

return 0;

}

加分二叉樹 區間DP,記憶化搜尋)

設乙個n個節點的二叉樹tree的中序遍歷為 1,2,3,n 其中數字1,2,3,n為節點編號。每個節點都有乙個分數 均為正整數 記第i個節點的分數為didi,tree及它的每個子樹都有乙個加分,任一棵子樹subtree 也包含tree本身 的加分計算方法如下 subtree的左子樹的加分 subtr...

區間DP 加分二叉樹

題目 設乙個n個節點的二叉樹tree的中序遍歷為 1,2,3,n 其中數字1,2,3,n為節點編號。每個節點都有乙個分數 均為正整數 記第i個節點的分數為 d i tree及它的每個子樹都有乙個加分,任一棵子樹subtree 也包含tree本身 的加分計算方法如下 subtree的左子樹的加分 su...

二叉搜尋樹 二叉搜尋樹

題目 二叉搜尋樹 time limit 2000 1000 ms j a others memory limit 32768 32768 k j a others total submission s 6945 accepted submission s 3077 problem descripti...