标签:des style blog class code c
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
标签:des style blog class code c
原文地址:http://blog.csdn.net/xingyeyongheng/article/details/26009171