题面:
思路:
依然是一道很明显的区间dp
我们设$dp\left[i\right]\left[j\right]$表示前$j$个节点分成了$i$块的最小花费,$w\left[i\right]\left[j\right]$表示把闭区间$\left[i,j\right]$放在一起产生的价值
那么转移就比较明显了:
$dp\left[i\right]\left[j\right]=min\left(dp\left[i-1\right]\left[k-1\right]+w\left[k\right]\left[j\right]\right)$
$w$可以用前缀和维护以后$O\left(1\right)$计算,因为:
$w\left[i\right]\left[j\right]=\left(\left(\sum_{k=i}^{j}k\right)^2-\sum_{k=i}^{j}k^2\right)\div 2$
这样我们得到了一个复杂度为$O\left(n^2 m\right)$的dp,但是解决这道题还不够
把w函数的表达式展开可以发现,w满足四边形不等式,因此把里层枚举k的那部分优化掉就好了
Code:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 #define inf (1ll<<60ll) 7 using namespace std; 8 inline ll read(){ 9 ll re=0,flag=1;char ch=getchar(); 10 while(ch>‘9‘||ch<‘0‘){ 11 if(ch==‘-‘) flag=-1; 12 ch=getchar(); 13 } 14 while(ch>=‘0‘&&ch<=‘9‘) re=(re<<1)+(re<<3)+ch-‘0‘,ch=getchar(); 15 return re*flag; 16 } 17 ll n,m,a[1010],sum[1010],sqr[1010],s[1010][1010],dp[1010][1010]; 18 ll w(ll l,ll r){ 19 return ((sum[r]-sum[l-1])*(sum[r]-sum[l-1])-(sqr[r]-sqr[l-1]))/2ll; 20 } 21 int main(){ 22 ll i,j,k,tmp; 23 while((n=read())&&(m=read())){ 24 m++; 25 for(i=1;i<=n;i++) 26 a[i]=read(),sum[i]=sum[i-1]+a[i],sqr[i]=sqr[i-1]+a[i]*a[i]; 27 for(i=1;i<=n;i++) dp[1][i]=w(1,i),s[1][i]=1; 28 for(i=2;i<=m;i++){ 29 s[i][n+1]=n; 30 for(j=n;j>i;j--){ 31 dp[i][j]=inf; 32 for(k=s[i-1][j];k<=s[i][j+1];k++){ 33 if((tmp=dp[i-1][k-1]+w(k,j))<dp[i][j]){ 34 dp[i][j]=tmp;s[i][j]=k; 35 } 36 } 37 } 38 } 39 printf("%lld\n",dp[m][n]); 40 } 41 }