zoukankan      html  css  js  c++  java
  • [APIO2010]特别行动队

    一道斜率优化DP

    首先,什么是斜率优化:

    其实就是找斜率的方式将DP方程转换为y = kx+b的形式。

    如果对于方程形如这样的

    $ F[i] = min(F[j] + Sum[i,j]) + k $ (k为常数)

    我们不能对其进行比较有效果的优化,因为它的转移,涉及到了关于i和关于j的一些数组,这时我们就需要用斜率优化了。

    通常我们令k<j<i,且用j来更新F[i]比用j优。则有

    $ F[j] + sum[i,j] < F[k] + sum[i,k] $

    并且我们都可以化成如下形式,X[j],Y[j]指只关于j的数,X[k],Y[k]就是

    $ frac {Y[j] - Y[k]} {X[j] - X[k]} < F[i] $

    我们可以根据这个式子,来优化DP,这就是斜率优化的主要思想。

    $ ==这是分割线= $

    那我们来看这道题:

    设f[i]表示将前i个分组的最优值,则有转移方程式:

           f[i]=max{ f[j]+a*(C[i]-C[j])^2+b*(C[i]-C[j])+c }
    

    经过化简得到:

           f[i]=max{ (f[j]+a*C[j]^2-b*C[j])-2*a*C[i]*C[j] } + a*C[i]^2+b*C[i]+c
    

    单调队列维护上凸壳就行了

    这是代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    
    using namespace std;
    const int N=1000005;
    
    struct node{
    	ll x,y;
    	node(){}
    	node(ll x,ll y):x(x),y(y){}
    }st[N],a[N];
    int t,n;
    ll A,B,C;
    ll s[N],x[N],dp[N];
    
    double get(node a,node b){
    	return (double)(a.y-b.y)/(a.x-b.x);//算凸包上相邻两点的斜率.
    }
    
    ll _find(double k){//表示我们要找斜率大于等于k的上凸壳上最后一个点.
    	int l = 1,r = t;//假设凸包上有t个点,st[1~t].
    	while (l < r){
    		int mid = (l + r + 1)>>1;
    		if (get(st[mid],st[mid-1]) >= (double)k) l = mid;
    		else r = mid - 1;
    	}
    	return st[l].y - st[l].x * k;//k*x+b=y;求b. 
    }
    
    void solve(){//建上凸壳
    	t = 0; 
    	st[++t] = node(0,0);
    	for (int i = 1 ; i <= n ; i++){
    		dp[i] = _find(2ll * A * s[i]) + A * s[i] * s[i] + B * s[i] + C;
    		a[i].x = s[i]; 
    		a[i].y = dp[i] + A * s[i] * s[i] - B * s[i];
    		while (t>1 && get(a[i],st[t]) >= get(st[t],st[t-1])) t--;//表示如果i点加进去是凹的(st[t]到i的斜率>=st[t-1]到st[t]的斜率)那么就弹出st[t]
    		st[++t] = a[i];
    	}
    }
    
    int main(){
    	scanf("%d",&n);
    	scanf("%lld%lld%lld",&A,&B,&C);
    	for (int i = 1 ; i <= n ; i++) 
    		scanf("%lld",&x[i]);
    	for (int i = 1 ; i <= n ; i++) 
    		s[i] = s[i - 1] + x[i];
    	solve();
    	printf("%lld
    ",dp[n]);
    	return 0;
    }
    
  • 相关阅读:
    memento模式
    observe模式
    state模式
    Trie树的简单介绍和应用
    strategy模式
    全组和问题
    SRM 551 DIV2
    全排列问题
    TSE中关于分词的算法的改写最少切分
    template模式
  • 原文地址:https://www.cnblogs.com/Repulser/p/9592352.html
Copyright © 2011-2022 走看看