题目描述
传送门
题目大意:给出一个长度为n的序列,要求砍成m+1段,每段的权值是该区间中数的两两乘积之和,求划分的最小权值。
题解
预处理
w[i][j]
表示区间[i,j]中数的两两乘积之和
f[j][i]=min{f[k][i−1]+w[k+1][j]}
这个式子和poj 1160的DP转移式是一样的,所以用同样的方式优化DP即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1003
#define LL long long
using namespace std;
const LL inf=1e13;
LL dp[N][N],w[N][N],val[N],squ[N];
int n,m,s[N][N];
int main()
{
freopen("a.in","r",stdin);
while (true) {
scanf("%d%d",&n,&m);
if (!n&&!m) break;
m++;
memset(w,0,sizeof(w));
memset(dp,0,sizeof(dp));
for (int i=1;i<=n;i++) scanf("%I64d",&val[i]);
for (int i=1;i<=n;i++) squ[i]=squ[i-1]+val[i]*val[i];
for (int i=1;i<=n;i++) val[i]+=val[i-1];
for (int i=1;i<=n;i++)
for (int j=i;j<=n;j++)
w[i][j]=(val[j]-val[i-1])*(val[j]-val[i-1])-(squ[j]-squ[i-1]),w[i][j]/=2;
for (int i=1;i<=n;i++)
dp[i][1]=w[1][i],s[i][1]=0;
for (int i=2;i<=m;i++) {
s[n+1][i]=n;
for (int j=n;j>i;j--) {
dp[j][i]=inf;
for (int k=s[j][i-1];k<=s[j+1][i];k++) {
LL tmp=dp[k][i-1]+w[k+1][j];
if (tmp<dp[j][i])
dp[j][i]=tmp,s[j][i]=k;
}
}
}
printf("%I64d\n",dp[n][m]);
}
}