石子合併【區間dp】
阿新 • • 發佈:2019-01-28
有N堆石子排成一排,每堆石子有一定的數量。現要將N堆石子併成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過N-1次合併後成為一堆。求出總的代價最小值。
假設dp[1][4]表示將區間1~4的石子合併所花費的代價。dp[1][4]可以劃分為dp[1][1]+dp[2][4]、dp[1][2]+dp[3][4]、dp[1][3]+dp[4][4]。還可以往下繼續劃分。這是劃分的區間層次圖:
我們只需要用dp從下往上推就行了。我們可以用一個sum陣列來儲存一段區間內的合併代價。用k來表示分割點,嘗試區間內所有可能的分割,取代價最小的那個。
轉移方程:dp[begin][end]=dp[begin][k]+dp[k+1][end]+sum[end]-sum[begin-1] ;
程式碼:
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define inf 1<<30 int dp[210][210],sum[210],a[210]; int main() { int n; while(~scanf("%d",&n)) { for(int i=1; i<=n; i++) scanf("%d",&a[i]); sum[0]=0; for(int i=1; i<=n; i++) sum[i]=sum[i-1]+a[i]; for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) if(i==j) dp[i][j]=0; else dp[i][j]=inf; } for(int i=1; i<n; i++)//合併的次數 { for(int begin=1; begin+i<=n; begin++) { int end=i+begin; for(int k=begin; k<end; k++) { if(dp[begin][end]>dp[begin][k]+dp[k+1][end]+sum[end]-sum[begin-1]) dp[begin][end]=dp[begin][k]+dp[k+1][end]+sum[end]-sum[begin-1]; } } } printf("%d\n",dp[1][n]); } return 0; }