斜率优化DP
斜率优化DP 两步骤;
1.造出DP方程;
2.通过DP方程构造出一个斜率优化方程;
具体怎么构造呢? 如 两个点 a b;
构造出 DP(a)<DP(b) 然后通过这一式子,构造出一个下凸包(后面的边的斜率大于前面的边)
只有下凸包的点是可能作为状态转移方程的转移点,其他点都不可能。
然后,构造完后, 每一次更新的时候,就用head 和head+1两者进行比较,
如果DP(head+1)>DP(head) 那么将其出队 操作方法是 head++;
这里给出一道例题:
给出一个n个元素数组,将其分为几组,每一组算出其和的平方加上给定数m,将每一组的值求和然后输出,求最小值。
1 #include<cstdio> 2 #include<string.h> 3 using namespace std; 4 const int maxn=5e5+10; 5 int sum[maxn]; 6 int f[maxn]; 7 int q[maxn]; 8 int xie(int x,int y) 9 { //虽说这里命名为斜率,但是得结合下面的区间合才能是斜率; 10 return f[y]-f[x]+sum[y]*sum[y]-sum[x]*sum[x]; 11 } 12 int qu_num(int x,int y) 13 { //区间合; 14 return sum[y]-sum[x]; 15 } 16 void init() 17 { //优化过的初始化; 18 memset(f,0,sizeof(f)); 19 sum[0]=0; 20 } 21 int main() 22 { 23 int n,m,i; 24 while(scanf("%d%d",&n,&m)!=EOF){ 25 init(); 26 for(i=1;i<=n;i++){ 27 int t; 28 scanf("%d",&t); 29 sum[i]=sum[i-1]+t; //前缀和; 30 } 31 int head=0,tail=0; 32 for(i=1;i<=n;i++){ 33 int k=sum[i]*2; //这是斜率的一部分; 34 while(head<tail&&xie(q[head],q[head+1])<=k*qu_num(q[head],q[head+1])) 35 head++; //假如head+1比head优,就将head出队,直到不优为止; 36 //那为什么到不优的点之后,就可以停止了呢; 37 //判断条件:sum[i]*2小于斜率; 38 //因为这是一个凸包,斜率逐渐上升,既然head比head+1 39 //不优的话,那么再后面的点所能计算出的斜率与该点比较 40 //斜率会越来越大,而判断条件是:sum[i]*2小于斜率; 41 //所以当前点肯定比后面的点更优,故可以在此停止搜索; 42 //前提这是一个凸包; 43 f[i]=f[q[head]]+qu_num(q[head],i)*qu_num(q[head],i)+m; 44 while(head<tail){ 45 int a=xie(q[tail-1],q[tail])*qu_num(q[tail],i); 46 int b=xie(q[tail],i)*qu_num(q[tail-1],q[tail]); 47 if(a>=b) tail--; 48 else break; 49 //保证这是个向下凸包,为上面的搜索铺好前提; 50 //这道题的第一个点是原点,并且数据都是正整数,所以肯定能保证 51 //这是一个向下凸包; 52 } 53 tail++; 54 q[tail]=i; 55 } 56 printf("%d\n",f[n]); 57 } 58 return 0; 59 }