


4 1 4 5 1 2 4 2 4 5 1 2 0 0
17 2
/*分析:
假定dp[i][j]表示前i个数分成j段的最小值
cost[i]表示从1~i的数两两相乘的总和
sum[i]表示前i个数的和
则:
dp[i][j]=Min(dp[k][j-1]+cost[i]-cost[k]-sum[k]*(sum[i]-sum[k]))
=>dp[i][j]=dp[k][j-1]+cost[i]-cost[k]-sum[i]*sum[k]+sum[k]*sum[k]
由于有sum[i]*sum[k]这一项,所以不可能用单调队列维护
-cost[k]-sum[i]*sum[k]+sum[k]*sum[k]的最小值
所以我们要把sum[i]独立出来以便求维护单调队列是和i无关
现在我们需要找出最优的k,
令k2<k时k时最优的,即前k个数k为最优的取值
所以满足:
dp[k][j-1]+cost[i]-cost[k]-sum[i]*sum[k]+sum[k]*sum[k]
<=dp[k2][j-1]+cost[i]-cost[k2]-sum[i]*sum[k2]+sum[k2]*sum[k2]
=>(dp[k][j-1]-cost[k]+sum[k]*sum[k]-(dp[k2][j-1]-cost[k2]+sum[k2]*sum[k2]))/(sum[k]-sum[k2])<=sum[i]
设:
y1=dp[k][j-1]-cost[k]+sum[k]*sum[k]
x1=sum[k]
y2=dp[k2][j-1]-cost[k2]+sum[k2]*sum[k2]
x2=sum[k2]
所以变成:
(y2-y1)/(x2-x1)
即两点之间的斜率!
对于这个斜率我们怎么来寻找关系维护?
假定k点之前k最优且k点在单调队列的首位置
现在对于k+1点
首先对于队列中的点更新k+1之后的点是否能比k,k+1..更优,即更新最优点(因为sum[k+1]增加了所以可以更新最优点)
然后对于点k+1与队列中最后一个点的斜率必须是整个队列中斜率最大的,即保持斜率单调增加
为什么?因为假如k+1与k的斜率比k与k-1的斜率更低,
则随着sum[k+x]增加,k+1会首先更优于k,所以不需要k点,只需要比较k+1与k-1的点的优先值
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 99999999
typedef long long LL;
using namespace std;
const int MAX=1000+10;
int n,m,index,head,tail;
int s[MAX],q[MAX];
int dp[MAX][2],cost[MAX],sum[MAX];//dp采用滚动数组
int GetY(int k,int k2){
return dp[k][index^1]-cost[k]+sum[k]*sum[k]-(dp[k2][index^1]-cost[k2]+sum[k2]*sum[k2]);
}
int GetX(int k,int k2){
return sum[k]-sum[k2];
}
void DP(){
index=0;
memset(dp,0,sizeof dp);
for(int i=1;i<=n;++i)dp[i][index]=cost[i];
for(int j=1;j<=m;++j){//分成j段,j作为第一层循环才用滚动数组
index=index^1;
head=tail=0;
q[tail++]=0;
for(int i=1;i<=n;++i){
while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*sum[i])++head;
dp[i][index]=dp[q[head]][index^1]+cost[i]-cost[q[head]]-sum[i]*sum[q[head]]+sum[q[head]]*sum[q[head]];
while(head+1<tail && GetY(i,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i,q[tail-1]))--tail;
q[tail++]=i;
}
}
}
int main(){
while(~scanf("%d%d",&n,&m),n+m){
for(int i=1;i<=n;++i)scanf("%d",&s[i]);
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+s[i];
memset(cost,0,sizeof cost);
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j)cost[j]+=s[i]*s[j];
}
for(int i=1;i<=n;++i)cost[i]+=cost[i-1];
DP();
printf("%d\n",dp[n][index]);
}
return 0;
}
hdu2829之二维斜率优化DP,布布扣,bubuko.com
原文:http://blog.csdn.net/xingyeyongheng/article/details/26009171