zoukankan      html  css  js  c++  java
  • 斜率优化复习记

    由于我最近又脑残到了一定程度。所以滚回来复习一发斜率优化。

    大体经过是这个样子的。9.17晚上,打开DP优化PPT。斜率优化!我学过啊,随便推推吧。

    。。。一晚上过去了。。。

    cnm我是不是学了假的斜率优化。

    9.18耽搁了一天。9.19重新开开了这个题。然后上午收到了ACM区域赛过了的消息激动了一发。然后一直拖到下午才搞定这个题。

    ——————————————————分个鸽——————————————————

    先扔链接。https://www.lydsy.com/JudgeOnline/problem.php?id=3156

    由于我太蠢。最开始设的状态是f_i_,_0表示第i个位置不放塔的最小代价,f_i_,_1表示放塔。然后我们倒序做。起始状态f_n_,_1=A_n

    转移非常蠢。不写了。然后我们发现其实第二维并没有用。我们直接用f_i表示第i个位置强制放塔,然后过程中取答案即可。

    转移就是f_i=min(f_j+sum_i_+_1-sum_j-(j-i-1)*dis_j) 其中dis_i=n-i就是i到n的距离sum_i=sum_{j=i}^ndis_j就是后缀和

    然后就到了我们喜闻乐见的斜率优化了。

    我来大体描述一下十分玄学的斜率优化。

    一般来说它的状态转移是这个样子F_i =min /max(F_j+p_i*q_j)

    它大体可以转换成这种形式F_i+p_i*q_j=Fj(当然左右都可以有一些常数[已知项])

    是不是看起来很熟悉!!b+kx=y是不是!!!我们就把求F_i变成了求截距的极值!!!

    非常优秀对不对。但是分析起来贼难受。

    以这个题为例。

    化简一下式子:f_i=f_j+sum_{i+1}-sum_j-dis_j*j+dis_j*i+dis_j

    f_i-sum_{i+1}-dis_j*i=f_j-sum_j-dis_j*j+dis_j

    我们设i<j<k

    f_j+sum_{i+1}-sum_j-(j-i-1)*dis_j<f_k+sum_{i+1}-sum_k-(k-i-1)*dis_k

    然后移项得到f_j-sum_j-j*dis_j+dis_j-f_k+sum_k-k*dis_k-dis_k<i*(dis_k-dis_j)

    由于我们设的j<k所以dis_k-dis_j<0我们把它移到左边得到(Y_j-Y_k)/(dis_j-dis_k)>-iY就是那一大坨。

    至于为什么是-i是因为我们用(Y_j-Y_k)/(dis_j-dis_k)表示斜率很方便。

    我们利用队列维护这个斜率。当队首不够优的时候我们就弹出队首。也就是(Y_j-Y_k)/(dis_j-dis_k)<-i的时候弹出。

    接下来是队尾的处理。我们设j<k<l然后用g(k,j)表示jk的斜率,设g(l,k)>g(k,j)。我们进行分类讨论。如果g(l,k)<F_i那么很明显我们选l最优。如果g(l,k)>F_i我们有k优于l但是j又优于k。所以我们得出我们维护的是上凸壳。用斜率将不合法的队尾弹出即可。

    所以搞定啦QwQ。很奇怪对不对...我也这么觉得

    ——————————————————分个鸽——————————————————

    我再整理一下另一种理解方式。

    我们观察直线的变化特点。k=-i所以ki的增加而递减,所以我们可以得到我们维护的是上凸壳。

    同理可以得到上面的结论。

    ——————————————————分个鸽——————————————————

    一些容易错的地方...

    在推式子之前一定要分析好i,j,k的大小关系,不要像我一样蠢...导致不等式除负数没变号然后GG。然后维护的时候记得凸壳上必须留着一个点...不然也会GG...还有就是斜率优化第一步的状态转移方程一定要列对...我经常先不写细节先推完式子再补细节...然后就非常容易符号出错...

    好了没啥了留下代码我就跑路了。

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define maxn 1000010
    #define db double
    #define inf 20021225ll
    #define ll long long
    using namespace std;
    ll f[maxn],sum[maxn],dis[maxn],ans=inf*inf;
    int que[maxn],hd,tl,a[maxn];
    db calc(int x)
    {
    	return (db)f[x]-sum[x]-x*dis[x]+dis[x];
    }
    db slope(int j,int k)
    {
    	//printf("%lf %lf %lf
    ",calc(j),calc(k),(double)dis[j]-dis[k]);
    	return (calc(j)-calc(k))/((db)dis[j]-dis[k]);
    }
    int main()
    {
    	int n,i,j;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++)	scanf("%d",&a[i]);
    	for(i=n;i;i--)	dis[i]=n-i,sum[i]=sum[i+1]+dis[i];
    	f[n]=a[n];que[1]=n;hd=tl=1;ans=min(ans,f[n]+sum[1]);sum[0]=sum[1];
    	for(i=n-1;i;i--)
    	{
    		while(hd<tl&&-slope(que[hd],que[hd+1])>i)	hd++;
    		//printf("%d %d %lf ",hd,tl,slope(que[hd],que[hd+1]));
    		j=que[hd];
    		f[i]=f[j]+sum[i+1]-sum[j]-dis[j]*(j-i-1)+a[i];
    		ans=min(ans,f[i]+sum[1]-sum[i]-dis[i]*(i-1));
    		//printf("%d %d %lld %lld
    ",i,j,f[i],f[i]+sum[1]-sum[i]-dis[i]*(i-1));
    		while(hd<tl&&slope(que[tl],i)<slope(que[tl-1],que[tl]))	tl--;
    		que[++tl]=i;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
  • 相关阅读:
    7、猜年龄
    6、continue语句
    5、break语句
    4、while循环练习
    poj 2378
    poj 2342
    poj 2287
    poj 2228
    poj 1191
    srm 578 dv2 1000pt
  • 原文地址:https://www.cnblogs.com/hanyuweining/p/10321976.html
Copyright © 2011-2022 走看看