【题目链接】
http://acm.hdu.edu.cn/showproblem.php?pid=3507
【解题报告】
花了两天时间,终于搞懂了斜率dp是怎么维护数据的,感觉自己理解力实在是弱的同时,也感叹确实难找到好的题解,有的题解甚至是错误的,比如百度这道题里面非常靠前的一篇blog,讲解思路混乱,同时给出了一个错误的结论:维护一个上凸点集。真不知道,他是如何在代码里维护的事下凸点集的情况里把题解错写成了维护上凸点集!这样混乱的错误的题解不仅不能给予人启发,反而会使得人们更加糊涂,不得算法要害,白白浪费时间。
找了两篇觉得不错的,适合配合起来阅读。
http://blog.sina.com.cn/s/blog_508dd1e60100tvk0.html
————Chris_Home
这篇十分形象,可以帮助理解为什么要维护一个下凸点集。
http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657650.html
————kuangbin
bin神的题解,对于如何在一个单调队列里找到i的解,并且如何把i插入到队列中,以及其正确性的说明都非常清楚。
加一点我自己的想法就是,对于这种题目,可能思路出来了,写也要琢磨琢磨,如何把代码写的可读性强,逻辑清楚,便于维护。比如在这里,要把dp值和sum值转化为对应的x和y坐标,那么单独把这个功能拿出来写一个模块是明智的做法。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<deque>
using namespace std;
int N,M;
int sum[500000+100];
int dp[500000+100];
deque<int>q;
void get_DP( int i,int j )
{
dp[i]=dp[j]+ (sum[i]-sum[j])*(sum[i]-sum[j])+M;
}
int Y( int j,int k )
{
return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];
}
int X( int j,int k )
{
return 2*( sum[j]-sum[k] );
}
int main()
{
while( ~scanf( "%d%d",&N,&M ) )
{
memset( sum,0,sizeof sum );
memset( dp,0,sizeof dp );
for( int i=1; i<=N; i++ ) { scanf( "%d",&sum[i] ); sum[i]+=sum[i-1]; }
if( !N ){ printf( "%d\n",M ); continue; }
q.clear();
q.push_back(0);
for( int i=1; i<=N; i++ )
{
while( q.size()>1 && Y( q[1],q[0] )<=sum[i]*X( q[1],q[0] ) ) q.pop_front(); //第二个比第一个优
int pos=q.front();
get_DP( i,pos );
while( q.size()>1 )
{
int j=q.size()-1, k=q.size()-2;
if( Y( i,q[j] )*X( q[j],q[k] )>Y(q[j],q[k])*X(i,q[j]) )break; //之前这里错写成了j和k,wa了无数次...
else q.pop_back();
}
q.push_back(i);
}
printf( "%d\n",dp[N] );
}
return 0;
}