石子合併(弱化版)
阿新 • • 發佈:2022-01-07
題目大意
設有 \(N(N \le 300)\) 堆石子排成一排,其編號為 \(1,2,3,\cdots,N\)。每堆石子有一定的質量 \(m_i(m_i \le 1000)\)。
現在要將這 \(N\) 堆石子合併成為一堆。每次只能合併相鄰的兩堆,合併的代價為這兩堆石子的質量之和,合併後與這兩堆石子相鄰的石子將和新堆相鄰。合併時由於選擇的順序不同,合併的總代價也不相同。
試找出一種合理的方法,使總的代價最小,並輸出最小代價。
題目分析
辨別區間 \(\rm DP\) 的方式:
從小區間延伸到大區間(合併等方式)。
許多括號數數題就是區間 \(\rm DP\) 好題,今年 \(\verb!2021-CSP-S-括號序列!\)
令 \(dp[l][r]\) 表示區間 \([l,r]\) 內合併的最小代價。
列舉左右區間,再列舉中間斷點來判斷是否答案會變得更優:
\(dp[l][r]=\min\{dp[l][k]+dp[k+1][r]+cost\}(l\le k\lt r)\)。
其中 \(cost\) 表示 \(\sum\limits_{i=l}^{r}a_i\)。
列舉區間(左右+斷點)複雜度為 \(O(N^3)\),中間列舉價值的部分又是 \(O(N)\),總時間複雜度 \(O(N^4)\)。
列舉價值部分可以通過字首和優化,優化為 \(O(N^3)\)。
程式碼
//2022/1/6 const int INF=0x3f3f3f3f; const int ma=305; int a[ma],sum[ma]; int dp[ma][ma]; int n; int main(void) { n=read(); Input_Int(n,a); for(register int i=1;i<=n;i++) { sum[i]=sum[i-1]+a[i]; } for(register int len=2;len<=n;len++) { for(register int l=1;l+len-1<=n;l++) { int r=l+len-1; dp[l][r]=INF; for(register int k=l;k<r;k++) { int w=sum[r]-sum[l-1]; dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+w); } } } printf("%d\n",dp[1][n]); return 0; }