282. 石子合併
阿新 • • 發佈:2020-09-11
狀態轉移方程:
\(f(i, j) = min\{s[j] - s[l - 1] + f[i][k] + f[k + 1][j]\} (k = i, i + 1, ..., r - 1)\)
第一種(迴圈區間長度和左端點)
#include<iostream> using namespace std; const int N = 310, INF = 0x3f3f3f3f; int f[N][N]; int a[N], s[N]; int n; int main(){ cin >> n; for(int i = 1; i <= n; i ++) cin >> a[i]; for(int i = 1; i <= n; i ++) s[i] = a[i] + s[i - 1]; /* 1. 合併一堆石子的代價為0 <=> f[i][i] = 0 2. len必須從2開始否則得到的矩陣會是[INF]nxn 3. 使用列舉兩個區間端點的時候需要考慮保證之前的狀態被計算過 */ for(int len = 2; len <= n; len ++) for(int l = 1; l + len - 1 <= n; l ++){ int r = l + len - 1; f[l][r] = INF; for(int k = l; k < r; k ++) f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]); } cout << f[1][n]; return 0; }
第二種(迴圈兩個區間端點)
#include<iostream> using namespace std; const int N = 310, INF = 0x3f3f3f3f; int f[N][N]; int a[N], s[N]; int n; int main(){ cin >> n; for(int i = 1; i <= n; i ++) cin >> a[i]; for(int i = 1; i <= n; i ++) s[i] = a[i] + s[i - 1]; /* k + 1必定大於i,而此時f[k + 1][j]還沒有被計算, 這就是為什麼迴圈i要倒著來的原因了。 */ for(int i = n; i >= 1; i --) for(int j = i + 1; j <= n; j ++){ f[i][j] = INF; for(int k = i; k < j; k ++) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); } cout << f[1][n]; return 0; }