石子合併 動態規劃(環形)
阿新 • • 發佈:2019-02-02
1、問題描述:問題來源 NWPU noj 1148
在一個圓形操場的四周擺放著n堆石子(n<= 100),現要將石子有次序地合併成一堆。規定每次只能選取相鄰的兩堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。編一程式,讀入石子堆數n及每堆的石子數(<=20)。選擇一種合併石子的方案,使得做n-1次合併,得分的總和最小; 比如有4堆石子:44 5 9 則最佳合併方案如下:
4 4 5 9 score: 0
8 5 9 score: 8
13 9 score: 8 + 13 = 21
22 score: 8 + 13 + 22 = 43
2、輸入
可能有多組測試資料。 當輸入n=0時結束! 第一行為石子堆數n(1<=n<=100);第二行為n堆的石子每堆的石子數,每兩個數之間用一個空格分隔。
3、輸出
合併的最小得分,每個結果一行。
4、問題解析
這個問題和直線型的區別在於最後一堆和第一堆也是相鄰的,可以把圓形轉換成直線型,把問題擴充套件為2n-1堆石子,舉個例子,如果環形石子堆是4 4 5 9,那麼轉換成直線型就變成了 4 4 5 9 4 4 5,所以最終就不是計算 0~n-1了,而是在 0~n-1,1-n,2-n+1,...,n-1~2n-2中選擇最小的。計算方法和直線型的相同。
5、程式碼如下:(已經提交過驗證)
#include<iostream> #include<vector> using namespace std; int main() { int n;//n堆石子 int i, j, k, sum, tmp;//輔助變數 while (cin >> n) { if (0 == n)break; sum = 0; tmp = INT_MAX; vector<int> arr(2*n-1); vector<vector<int> >min(2*n-1, vector<int>(2*n-1));//min[i][j]代表合併第i堆到第j堆的最小得分 for (i = 0; i < n; ++i) { cin >> arr[i]; } for (i = n; i <= 2*n - 2; ++i) { arr[i] = arr[i - n]; } //給min賦值 for (i = 0; i <= 2*n-2; ++i) { min[i][i] = 0;//初始化為0 } for (i = 2*n - 3; i >= 0; --i) { for (j = i + 1; j <= 2*n-2; ++j) { for (k = i; k <= j; ++k) { sum += arr[k]; // cout << "sum的值:" << sum << endl; }//end for k for (k = i; k < j; ++k) { if (tmp > min[i][k] + min[k + 1][j]) { tmp = min[i][k] + min[k + 1][j]; } }//end for k min[i][j] = sum + tmp; sum = 0; tmp = INT_MAX; }// end for j }// end for i //選出長度為n的最小的值 int Min = INT_MAX; for (i = 0; i < n; ++i) { if (min[i][i + n - 1] < Min) Min = min[i][i + n - 1]; } cout <<Min << endl; Min = INT_MAX; } }