【动态规划】 石子合并问题(环形) (ssl 1597)
生活随笔
收集整理的這篇文章主要介紹了
【动态规划】 石子合并问题(环形) (ssl 1597)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
石子合并問題石子合并問題石子合并問題
Description
在一個圓形操場的四周擺放著n 堆石子。現要將石子有次序地合并成一堆。規定每次只能選相鄰的2 堆石子合并成新的一堆,并將新的一堆石子數記為該次合并的得分。試設計一個算法,計算出將n堆石子合并成一堆的最小得分和最大得分。
編程任務:
對于給定n堆石子,編程計算合并成一堆的最小得分和最大得分。
Input
輸入包括多組測試數據,每組測試數據包括兩行。
第1 行是正整數n,1<=n<=100,表示有n堆石子。
第2行有n個數,分別表示每堆石子的個數。
Output
對于每組輸入數據,輸出兩行。
第1 行中的數是最小得分;第2 行中的數是最大得分。
Sample Input
4
4 4 5 9
Sample Output
43
54
題目大意:
有n堆石子,圍成一個環,可以將相鄰的兩堆合在一起,兩堆的重量之和為你的分數,要求最大分數和最小分數
解題方法:
建議先做完石子合并(非環形)題解,再做此題,本體我們有兩種方法:
方法一方法一方法一
我們先用一個二位數組f[i][j]來表示從第i對開始,后面的j個數的最小值(最大的用l),然后在后面復制一遍接下來就和石子合并差不多了
動態轉移方程:
f[i][len]=min(f[i][len],f[i][k]+f[i+k][len?k]+s[i+len?1]?s[i?1])f[i][len]=min(f[i][len],f[i][k]+f[i+k][len-k]+s[i+len-1]-s[i-1])f[i][len]=min(f[i][len],f[i][k]+f[i+k][len?k]+s[i+len?1]?s[i?1])
l[i][len]=max(l[i][len],l[i][k]+l[i+k][len?k]+s[i+len?1]?s[i?1])l[i][len]=max(l[i][len],l[i][k]+l[i+k][len-k]+s[i+len-1]-s[i-1])l[i][len]=max(l[i][len],l[i][k]+l[i+k][len?k]+s[i+len?1]?s[i?1])
注釋:
f[i][k]為前面,i+k是后面的開始,len-k是長度有len已經用了k,所以要減掉k,i+len-1是這一段的后面,因為第i個也要求,所以要減去i-1
#include<cstdio> #include<iostream> using namespace std; int a[205],f[205][205],l[205][205],s[205],n,minn,maxx; int main() {memset(f,127/3,sizeof(f));scanf("%d",&n);for (int i=1;i<=n;i++){scanf("%d",&a[i]);s[i]=s[i-1]+a[i];f[i][1]=0;}for (int i=n+1;i<=n*2;i++){s[i]=s[i-1]+a[i-n];//復制一遍f[i][1]=0;}for (int len=2;len<=n;len++)//長度for (int i=1;i<=n*2-len;i++)//前面的數,n-len+1+n-1=n*2-lenfor (int k=1;k<len;k++)//分界線{f[i][len]=min(f[i][len],f[i][k]+f[i+k][len-k]+s[i+len-1]-s[i-1]);//求最小的l[i][len]=max(l[i][len],l[i][k]+l[i+k][len-k]+s[i+len-1]-s[i-1]);//求最大的}minn=2147483647;for (int i=1;i<=n;i++){minn=min(minn,f[i][n]);//最小的maxx=max(maxx,l[i][n]);//最大的}printf("%d\n%d",minn,maxx); }方法二方法二方法二
我們不在后面加一段(f和l表示的一樣),直接用mod的方法,超過你的直接從1開始往后,本做法詳情看動態轉移方程
動態轉移方程
f[i][len]=min(f[i][len],f[i][k]+f[(i+k?1)modn+1][len?k]+fj(i,i+len?1));f[i][len]=min(f[i][len],f[i][k]+f[(i+k-1)modn+1][len-k]+fj(i,i+len-1));f[i][len]=min(f[i][len],f[i][k]+f[(i+k?1)modn+1][len?k]+fj(i,i+len?1));
l[i][len]=max(l[i][len],l[i][k]+l[(i+k?1)modn+1][len?k]+fj(i,i+len?1));l[i][len]=max(l[i][len],l[i][k]+l[(i+k-1)modn+1][len-k]+fj(i,i+len-1));l[i][len]=max(l[i][len],l[i][k]+l[(i+k?1)modn+1][len?k]+fj(i,i+len?1));
注釋:
(i+k-1) mod n+1為什么不寫成(i+k)mod n呢?因為當i+k=n時,結果為0,0不在我們的計算范圍內,所以我們要用(i+k-1) mod n+1,用這種的話,當i+k=n時,結果為n。fj(i,i+len-1)為計算i到i+len-1之間的數
#include<cstdio> #include<iostream> using namespace std; int a[105],f[105][105],l[105][105],s[105],n,minn,maxx; int fj(int x,int y) {if (y<=n) return s[y]-s[x-1];//沒有超過nreturn fj(x,n)+fj(1,y-n);//分兩段 } int main() {memset(f,127/3,sizeof(f));scanf("%d",&n);for (int i=1;i<=n;i++){scanf("%d",&a[i]);s[i]=s[i-1]+a[i];f[i][1]=0;}for (int len=2;len<=n;len++)//長度for (int i=1;i<=n;i++)//前面的for (int k=1;k<len;k++)//分界線{f[i][len]=min(f[i][len],f[i][k]+f[(i+k-1)%n+1][len-k]+fj(i,i+len-1));//狀態轉移方程l[i][len]=max(l[i][len],l[i][k]+l[(i+k-1)%n+1][len-k]+fj(i,i+len-1));//狀態轉移方程}minn=2147483647;for (int i=1;i<=n;i++){minn=min(minn,f[i][n]);//求最小的maxx=max(maxx,l[i][n]);//求最大的}printf("%d\n%d",minn,maxx); } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【动态规划】 石子合并问题(环形) (ssl 1597)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 金士顿真假内存鉴别 有四种辨别方法
- 下一篇: 【动态规划】拔河比赛 (ssl 1638