7-10 石子合并 (10 分)
一:前言:
關于石子合并,這個問題分為鏈型和環形兩種,本題當中用的是環形,但我們在學習動態規劃,所以多練肯定是有好處的,況且將鏈型的問題解決后,更容易理解環形的解決方法,所以本次題解分為兩部分鏈型和環型兩部分
二:石子合并(鏈型)
1.題目
題目】
設有N堆石子排成一排,其編號為1,2,3,
每堆石子有一定的質量,可以用一個整數來描述,現在要將這N堆石子
合并成為一堆。每次只能合并相鄰的兩堆,合并的代價為這兩堆石子的質
量之和,合并時由于選擇的順序不同,含并的總代價也不相同。
問題是:找出一種合理的方法,使總的代價最小,輸出最小代價。
輸入格式】
第一行一個數,N表示石子的堆數。1≤N≤300
第二行N個數,表示每堆石子的質量(均不超過1000
輸出格式】
輸出一個整數,表示最小代價。
【輸入樣例】
【輸出樣例
342:思路:
思路:
1.判斷動態規劃
這里最終的結果,其求解過程 也是跳躍性的,所以還是動態規劃
2.分析題意
并且這里涉及到網格(就是建立二維數組),根據合并不同的堆
其合并的和是不一樣的,故這里有涉及到劃分,所以我們又可以
聯想到相似的解法(矩陣鏈相乘)
3.求其遞推方程
其遞推方程:m[i][j] = m[i][k]+m[k+1][j] + sum [j] - sum [i -1];
4.遞推方程的解釋:
前面的m[i][k] 和 m[k+1][j] :i到k合并成一堆,k+1到j又合并成一堆
后面的sum[j] - sum[i -1]: 這里表示的是剛形成的兩堆 再合并到一塊的石子數
也就是用前綴和來求區間和
比如有(1,2,3,4,5,6)這里表示的是有6堆石子
現在我們求m[3][6] = m[3][4]+m[5][6] + sum[6] - sum[2]
3.上碼
/**思路:1.判斷動態規劃 這里最終的結果,其求解過程 也是跳躍性的,所以還是動態規劃2.分析題意 并且這里涉及到網格(就是建立二維數組),根據合并不同的堆其合并的和是不一樣的,故這里有涉及到劃分,所以我們又可以聯想到相似的解法(矩陣鏈相乘)3.求其遞推方程 其遞推方程:m[i][j] = m[i][k]+m[k+1][j] + sum [j] - sum [i -1];4.遞推方程的解釋:前面的m[i][k] 和 m[k+1][j] :i到k合并成一堆,k+1到j又合并成一堆后面的sum[j] - sum[i -1]: 這里表示的是剛形成的兩堆 再合并到一塊的石子數也就是用前綴和來求區間和 比如有(1,2,3,4,5,6)這里表示的是有6堆石子現在我們求m[3][6] = m[3][4]+m[5][6] + sum[6] - sum[2] sum[6]:表示的是將這6堆全部合并在一起 sum[2]:表示的是將前兩堆合并在一起現在我們要求后4堆的和 那不就是sum[6] - sum [2]; */// 我們要求的是最小次數 #include<bits/stdc++.h> using namespace std; #define infinite 99 int main(){int N;int m[100][100];//注意二維數組不能開的太大 cin >> N;int sum[N+1];//求前綴和int a[N+1];//記錄每堆的石子數量 //因為我們要求的是最小次數 故我們將數組先進行賦最大值for(int i = 0; i < 100; i++){for(int j = 0; j < 100; j++){m[i][j] = infinite; }} for(int i = 1; i <= N; i++){cin >> a[i];sum[i] = sum[i-1] + a[i];//記錄前綴和 m[i][i] = 0;//自己 到自己肯定不能合并啊 }// for(int i = 0; i <= N; i++){ // cout << sum[i] << ' '; // } // //開始更新網格for(int i = N; i >= 1; i--){for(int j = i + 1; j <= N; j++){//這里的i+1是指的是在i后面進行劃分 m[i][j] = m[i][i] + m[i+1][j] + sum[j] - sum[i - 1];for(int k = i+1; k < j; k++){//這里的i+1指的是在i后面進行劃分 k < j 指的是k在j之前劃分 int temp = m[i][k] + m[k+1][j] + sum[j] - sum[i-1];if(temp < m[i][j]){m[i][j] = temp;}} }} // for(int i = 1; i <= N; i++){ // for(int j = 1; j <= N; j++){ // cout << m[i][j] << ' ' << ' ' << ' ' << ' '; // } // cout << endl; // }cout << m[1][N]; }//測試用例一: //5 //1 3 4 2 5//34//測試用例二: //4 //4 5 9 4 //0 9 27 44 //0 0 14 31 //0 0 0 13 //0 0 0 0//44三:石子合并(環形 (本題題解7-10))
1.題目:
在一個圓形操場的四周擺放 N 堆石子,現要將石子有次序地合并成一堆.規定每次只能選相鄰的 2 堆合并成新的一堆,并將新的一堆的石子數,記為該次合并的得分。
試設計出一個算法,計算出將 N 堆石子合并成 1 堆的最小得分和最大得分。
輸入格式:
數據的第 1 行是正整數 N ,表示有 N 堆石子。
第 2 行有 N 個整數,第 i 個整數 a
i
?
表示第 i 堆石子的個數。
輸出格式:
輸出共 2 行,第 1 行為最小得分,第 2 行為最大得分。
輸入樣例:
4 4 5 9 4輸出樣例:
43 542.思路:
思路:這里是環形的,我們把環形的每次連接的地方依次剪開,變成鏈型的
那么就有一種思路 就是將輸入的數組復制兩次,那么的話,我們每次一次
取出N個元素,就實現了,不同的鏈接處的分割,然后求出最后結果時候
根據劃分的不同我們按最大或或最小值選取最后的結果
3.上碼:
/**思路:這里是環形的,我們把環形的每次連接的地方依次剪開,變成鏈型的那么就有一種思路 就是將輸入的數組復制兩次,那么的話,我們每次一次取出N個元素,就實現了,不同的鏈接處的分割*/ #include<bits/stdc++.h> using namespace std;int main(){int N;int max_tone[300][300];int min_tone[300][300];cin >> N;int a[2*N+1];int sum[2*N+1];sum[0] = 0;//需要給sum[0] 賦初值,否則sum[0] 是個很大的數 // //注意下方的代碼:數組的列均變為原來的2倍 行是不變的 // for(int i = 0; i <= N; i++){ // for(int j = 0; j <= 2*N; j++){ // max_tone[i][j] = 0; // min_tone[i][j] = 9999; // } // }for(int i = 1; i <= N; i++){cin >> a[i];a[i+N] = a[i];}for(int i = 1; i <= 2*N; i++){sum[i] = sum[i-1] + a[i];max_tone[i][i] = 0;min_tone[i][i] = 0;}// for(int i = 0; i <= 2*N; i++){ // cout << sum[i] << ' '; // } // //開始更新網格for(int i = 2*N; i >= 1; i--){for(int j = i+1; j < i+N && j <= 2*N; j++){//這里的j < i+N,是確定每次劃分的范圍為N個 max_tone[i][j] = max_tone[i][i] + max_tone[i+1][j] + sum[j] - sum[i-1];min_tone[i][j] = min_tone[i][i] + min_tone[i+1][j] + sum[j] - sum[i-1];for(int k = i + 1; k < j; k++){int temp1 = max_tone[i][k] + max_tone[k+1][j] + sum[j] - sum[i-1];int temp2 = min_tone[i][k] + min_tone[k+1][j] + sum[j] - sum[i-1];//求取最大值 if(temp1 > max_tone[i][j]){max_tone[i][j] = temp1;} //求取最小值if(temp2 < min_tone[i][j]){min_tone[i][j] = temp2;} } } } // // for(int i = 1; i <= N; i++){ // for(int j = 1; j <= 2*N; j++){ // cout << min_tone[i][j] << ' '; // } // cout << endl; // }//因為 我們從合并的兩個數組中每次選取出N個元素,即1到N,2到N+1,N到i+N-1為止,再往下就出現重復了int maxx = 0,minn = 9999;for(int i = 1; i <= N; i++){maxx = max(max_tone[i][i+N-1],maxx);minn = min(min_tone[i][i+N-1],minn); } cout << minn << endl << maxx; } //4 //4 5 9 4//0 9 27 44 0 0 0 0 //0 0 14 31 44 0 0 0 //0 0 0 13 25 43 0 0 //0 0 0 0 8 21 43 0//0 9 27 36 0 0 0 0 //0 0 14 23 35 0 0 0 //0 0 0 9 17 31 0 0 //0 0 0 0 4 13 31 0這個圖是給我自己看的 太亂了啊 方便我理解當時候我的思路 怕草稿本子丟了,找不到當初的想法了
四:總結
如果看到我前面的題解,你會發現,代碼思路幾乎一致,都是劃分網格,推出遞推方程,本題的題解其實和前面的矩陣連相乘思路相似,所以兄弟們,請認真做每一道題,思路都是融會貫通的,算法題本來就很難理解,所以需要很長時間去打磨,注意題解當中的每次的取值范圍,均需想明白!!
五:數據結構和算法分析思維導圖
如果想看一下的我們知識體系, 我分享一張我收藏的思維導圖(僅供參考)
加油boy!成功本就不易,我們共勉 有疑問可以留言!!!
總結
以上是生活随笔為你收集整理的7-10 石子合并 (10 分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国首次掺氢天然气管道燃爆试验成功,最大
- 下一篇: 7-3 凸多边形最优三角剖分 (10 分