有\(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...