区间dp
#include <bits/stdc++.h> using namespace std; #define maxn 2000 const int maxm=5000; int f[maxn][maxn],a[maxm],sum[maxm],n; inline int read(){ int num=0,f=1; char c=getchar(); while(!isdigit(c)){if(c=='-') f=-1; c=getchar();} while(isdigit(c)){num=((num<<1)+(num<<3))+(c^48); c=getchar();} return num*f; } int in(){ memset(f,10,sizeof(f)); n=read(); for(int i=1;i<=n;i++){ a[i]=read(); sum[i]+=sum[i-1]+a[i]; f[i][i]=0; } } int work(){ for(int l=1;l<=n;l++){ for(int i=1;i+l<=n;i++){ int j=i+l; for(int k=i;k<i+l;k++){ f[i][j]=min(f[i][k]+f[k+1][j]+sum[j]-sum[i-1],f[i][j]); } } } cout<<f[1][n]; } int main(){ in(); work(); } 区间dp(大佬勿喷) 有n堆石子排成一排,每堆石子都有一个大小。每次你可以合并相邻两堆石子,消耗的体力为两堆石子大小总和,合并后的石子位置为原来两堆石子的位置,合并后的石子大小为两堆石子大小总和。 求最小的总体力消耗。 n<=100 进阶数据范围:n<=1000 我们注意到,每堆石子都一定是由一段连续的区间合并而来。 且一段区间合并后的总大小是确定的。 于是我们设dp[l][r]表示区间[l,r]之间的石子合并成一堆所需要的最小体力。 考虑最后一次合并,一定只剩2堆石子。 而这2堆石子一定也是由连续的区间合并而来的。 所以我们可以枚举左边区间的右端点(右边区间的左端点)。 for (int k=l;k<r;++k) dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+sum(l,r)); 其中sum(l,r)表示区间[l,r]的总和。可以通过预处理来支持快速查询。 状态:O(n^2);转移:O(n)。总复杂度:O(n^3)。