zoukankan      html  css  js  c++  java
  • 斜率优化 && 洛谷 P2365 任务安排

    传送门


    斜率优化

    当求dp[i]最小值时,满足一次函数 (y=kx+b)
    其中 y=dp[j] ,k 与 i 有关,x 与 j 有关,b中包含 dp[i]。
    这时问题就可以转化为 在众多点(x,y)中找到一个点使得用斜率为k的直线切这个点时得到的截距b最小。很显然这时dp[i]从这个点转移过来最优。
    具体操作是维护一个相邻两点斜率单调递增的下凸壳
    用单调队列维护。
    以求dp[i]为例。
    先在单调队列中三分找到两点间斜率刚好>=k的点,然后比较一下取最优点进行转移。
    然后不断判断队尾点是否符合斜率要求并不断弹出,最后将i插入到队尾。
    总复杂度为O(nlogn)
    实际上就是把枚举j的复杂度O(n)优化到了O(logn)。
    当然有的题目k是单调递增的,这时候就可以每次用k和队首两个点比较并不断弹出队首,每次更新时队首的点即为最优点,这样也省去了三分的复杂度。
    这个题就是这个特殊情况。

    AC代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<iomanip>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn=5005;
    int n,s;
    long long dp[maxn],sumt[maxn],sumf[maxn],q[maxn],l=1,r=1;
    inline double cal(int l,int r){
    	return 1.0*(dp[r]-dp[l])/(sumf[r]-sumf[l]);
    }
    int main(){
    	ios::sync_with_stdio(false);
    	cin>>n>>s;
    	for(int i=1;i<=n;i++){
    		cin>>sumt[i]>>sumf[i];
    		sumt[i]+=sumt[i-1];
    		sumf[i]+=sumf[i-1];
    	}
    	dp[0]=q[1]=0;
    	for(int i=1;i<=n;i++){
    		while(l<r&&dp[q[l+1]]-dp[q[l]]<=(s+sumt[i])*(sumf[q[l+1]]-sumf[q[l]])) l++;
    		dp[i]=dp[q[l]]+s*(sumf[n]-sumf[q[l]])+sumt[i]*(sumf[i]-sumf[q[l]]);
    		while(l<r&&cal(q[r-1],q[r])>=cal(q[r],i)) r--;
    		q[++r]=i;
    	}
    	cout<<dp[n];
        return 0;
    }
    
  • 相关阅读:
    ZOJ 3556
    ZOJ 2836
    HDU 2841
    HDU 4135
    POJ 3695
    POJ 2773
    HDU 4407
    HDU 1796
    ZOJ 3688
    ZOJ 3687
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/15256311.html
Copyright © 2011-2022 走看看