zoukankan      html  css  js  c++  java
  • 20201113 Day3 斜率优化

    1 玩具装箱[HNOI2008]

    solution

    回到本题,设前缀和为(sum[i]),由题意易得dp方程:

    [dp[i]=min(dp[j]+(sum[i]+i-sum[j]-j-L-1)^2) (j<i) ]

    但这个方程是(O(n^2))的,显然不满足要求,因此需要进行优化

    (以下称两点斜率为(slope(A,B))

    (a[i]=sum[i]+i)(b[i]=sum[i]+i+L+1)(这一步是为了简化计算)

    [dp[i]=dp[j]+(a[i]-b[j])^2 ]

    展开得

    [dp[i]=dp[j]+a[i]^2-2 cdot a[i] cdot b[j]+b[j]^2 ]

    移项得

    [2 cdot a[i] cdot b[j]+dp[i]-a[i]^2=dp[j]+b[j]^2 ]

    (b[j])看作(x)(dp[j]+b[j]^2)看作(y),这个式子就可以看作一条斜率为(2 cdot a[i])的直线

    而对于每个(i)来说,(a[i])都是确定的

    接下来的步骤和线性规划很相似

    (dp[i])的含义转化为:当上述直线过点(P(b[j],dp[j]+b[j]^2))时,直线在(y)轴的截距加上(a[i]^2)(一个定值)

    而题目即为找这个截距的最小值

    因此,类似线性规划,我们将这条直线从下往上平移,直到过一个符合要求的点时停下,此时截距即为最小

    code

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn=5e5+10;
    const int mod=1e4+7;
    #define int long long
    int read(){
    	int a=0,op=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') op=-1;c=getchar();}
    	while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
    	return a*op;
    }
    int n,L;
    double sum[maxn],dp[maxn];
    int head,tail,q[maxn];
    double a(int x){return sum[x]+x;}
    double b(int x){return a(x)+L+1;}
    double X(int x){return b(x);}
    double Y(int x){return dp[x]+b(x)*b(x);}
    double k(int A,int B){return (Y(A)-Y(B))/(X(A)-X(B));}
    double d_read(){
    	double p;scanf("%lf",&p);return p;
    }
    #undef int
    int main(){
    #define int long long
    	n=read(),L=read();
    	for(int i=1;i<=n;i++) sum[i]=d_read(),sum[i]+=sum[i-1];
    	head=tail=1;
    	for(int i=1;i<=n;i++){
    		while(head<tail&&k(q[head],q[head+1])<2*a(i)) ++head;
    		dp[i]=dp[q[head]]+(a(i)-b(q[head]))*(a(i)-b(q[head]));
    		while(head<tail&&k(i,q[tail-1])<k(q[tail-1],q[tail])) --tail;
    		q[++tail]=i;
    	}
    	printf("%lld
    ",(int)dp[n]);
    	return 0;
    }
    
    

    2 任务安排

    solution

    方程:(f[i])代表前(i)个任务分成若干批产生的最小费用

    转移:(f[i]=min_{i=0}^{i-1}(f[j]+t[i]*(c[i]-c[j])+S*(c[n]-c[j])))其中,t,ct,ct,c分别是任务时间和费用的前缀和的数组

    考虑将状态转移方程化简: (f[j]=(S+t[i])*c[j]+f[i]-t[i]*c[i]-S*c[n])

    注意:我们这里把(min)去掉了,是把(j)的取值集合所映射的(f[j])(c[j])分别作为函数的(f(x))(x)

    那么这个一次函数的斜率(k)就等于((S+t[i])),而截距(b)等于(f[i]-t[i]*c[i]-S*c[n])

    我们想让这个(f[i])最小,那么其实就等价于(b)最小,在坐标系中,如果我们拿一个已知斜率的直线向上滑动,当它第一次碰见取值集合内的点((c[j],f[j]))时,就取到了它的最小值

    code

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn=5e5+10;
    const int mod=1e4+7;
    #define int long long
    int read(){
    	int a=0,op=1;char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') op=-1;c=getchar();}
    	while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
    	return a*op;
    }
    int n,L;
    double sum[maxn],dp[maxn];
    double t[maxn],c[maxn];
    int head,tail,s,q[maxn];
    double d_read(){
    	double p;scanf("%lf",&p);return p;
    }
    double k(int a,int b){return (dp[a]-dp[b])/(c[a]-c[b]);}
    double b(int x){return t[x]*c[x]+s*c[n];}
    double a(int x){return s+t[x];}
    
    #undef int
    int main(){
    #define int long long
    	n=read(),s=read();
    	for(int i=1;i<=n;i++) t[i]=d_read(),c[i]=d_read();
    	for(int i=1;i<=n;i++) t[i]+=t[i-1],c[i]+=c[i-1];
    	dp[0]=0;
    	head=tail=1;
    	for(int i=1;i<=n;i++){
    		while(head<tail&&k(q[head],q[head+1])<=a(i)) ++head;
    		dp[i]=dp[q[head]]+b(i)-c[q[head]]*a(i);
    		while(head<tail&&k(i,q[tail])<k(q[tail-1],q[tail])) --tail;
    		q[++tail]=i;
    	}
    	printf("%lld
    ",(int)dp[n]);
    	return 0;
    }
    
    

    3 摆渡车

    solution

    [underline{f_j + sum_j}_{ y} = underline{i_{_{}}}_{ k} imes underline{cnt_j}_{ x} + underline{(f_i - cnt_i imes i + sum_i)}_{ b} ]

  • 相关阅读:
    windows下Mysql免安装版,修改my_default.ini配置文件无效的解决办法
    下压桟(LIFO)
    Dijkstra的双栈算术表达式求值算法
    获取中文的完整拼音并输出
    解析一个文件夹所有文件的中文,并输出到某一文本文档中
    在含有中英文字符串的信息中,提出中文的方法
    创建计算字段
    Docker 常用命令
    mqtt常用命令及配置
    LOG4J
  • 原文地址:https://www.cnblogs.com/liuziwen0224/p/20201113day3-001.html
Copyright © 2011-2022 走看看