背包重量
最高價值
1
8
9
5
4
0
背包重量
最高價值
1
8
5
4
背包重量
最高價值
1
8
9
5
4
0
0
L
R
L
R
i
i+1
題目:
給長度為n(<50)的正整數數列,每次操作可以合併相鄰的3個數,合併後中間的數被刪除,付出代價為三數相乘。
例如:相鄰三數為A, B, C則合併後剩下A, C操作代價為三數相乘AxBxC。
求合併到只剩2個數,操作過程代價總和最少為何
範例輸入:
1 2 3 4 5
範例輸出:
38
1 2 3 4 5
0
1 3 4 5
6
1 4 5
18
1 5
38
某一區間合併到最後會剩下最左邊和最右邊兩個數
dp[L][R]代表將區間[L,R]合併到剩下區間端點兩數的最小代價
L
R
L
R
i
i
i
L
i
R
觀察
合併到最後會剩下最左邊和最右邊兩個數
dp[L][R]代表將區間[L,R]合併到剩下區間端點兩數的最小代價
根據定義的dp陣列和觀察
#include <...>
const int N = ???;
int dp[N][N];
int DP(int l, int r) {
if(dp[l][r] != -1) // 已經 DP過
return dp[l][r];
if(邊界條件) {
dp[l][r] = 0;
return dp[l][r];
}
dp[l][r] = 1e9 + 1;
for(int i = l + 1; i < r; i++)
dp[l][r] = min(dp[l][r], DP(l, i) + DP(i, r) + cost(l, i, r));
return dp[l][r];
}
int main () {
// 輸入
memset(dp, -1, sizeof(dp));
printf("%d\n", DP(0, n - 1));
}#include <...>
const int N = ???;
int dp[N][N];
int main () {
// 輸入
memset(dp, 0, sizeof(dp));
for(int len = 3; len <= n; len++)
for(int l = 0; l + len - 1 < n; l++) {
int r = l + len - 1;
dp[l][r] = 1e9;
for(int i = l+1; i < r; i++)
dp[l][r] = min(dp[l][r], dp[l][i] + dp[i][r] + cost(l, i, r));
}
printf("%d\n", dp[0][n - 1]);
}時間複雜度:
題目大意:
權重由小到大給二元搜尋樹上每個葉子被訪問的次數e,每一次訪問的代價是葉子的深度d,所以一個葉子的總代價在二元樹中的代價為d*e,請找出一顆二元搜尋樹,使得訪問每個葉節點的總代價最低
因為
所以
結論
題目:
給2n(n<=8)個人,每個人位於平面上的一個點,希望將這2n個人組隊,每隊兩個人,組成一個隊伍的成本是兩個人之間的距離,求把所有人分組最小的成本
方法1:
用DFS直接爆搜,每層遞迴先選擇一個待分組的人當固定的成員,再依序枚舉沒有被分派組別的人給這個固定成員選組成一隊,繼續遞迴。
原理:
每個人最終會屬於某一組,所以每層遞迴可以固定一個人
1
2
3
4
1
2
3
4
5
6
7
8
5
6
7
8
#include <...>
...
void DP(int state) {
int fix;
for(fix = 0; fix < n; fix++)
if(state & (1 << fix)) // 如果第fix個bit不是0
break;
state &= ~(1 << fix); // 把第fix個bit變0
for(int i = 0; i < n; i++)
if(state & (1 << i)) {
int new_state = state & ~(1 << i); // 把第i個bit變0
if(new_state 還沒dp過) // 避免重複子問題
DP(new_state);
// state的最佳解可能是new_state的最佳解加上fix和i同組的cost
dp[state] = min(dp[state], dp[new_state] + cost(fix, i));
}
}
int main() {
...
// 輸入
DP((1 << n) - 1); // (1 << n) - 1 二進位會是n個1
cout << dp[(1 << n) - 1] << endl;
}