搞了一天的斜率优化,也算是有点入门吧。。。
斜率优化的题一般有个特点,在优化之前大都是区间dp的形式;
不过我只学了队列优化形式的,对于用栈来优化的还没有做过(蒟蒻逃
如果知道一个题是斜率优化dp,那么十有八九是做不错的,就我所做的裸的斜率优化dp大都遵循一个套路(那种套数据结构套cdq的另说)
首先必须要会推暴力的dp公式
然后再化简成d+kx=y的形式(没错我就是喜欢倒着写)
首先设要求的是i,从j这个点转移;
d是要求的dp值再加一堆乱七八糟的关于i恒定的值,总之就是知道d的值就能O(1)求出dp值就可以;
k是一个关于i的变量,应该要是一次的?(毕竟太高级的我也没做过
x是与j相关的变量;
y也是与j相关的变量;
就我所做的队列优化形式的dp来说,一般化简完以后x必须是单调的
而且k也要关于i单调;
这样就可以在凸包上寻找答案;
因为x是单调的,所以一定可以沿着一个方向来更新凸包,也不用担心会不会与被队列筛出去的点形成凸包;
因为k是单调的,所以就满足决策单调性……吧? 总之之前不能为i更新答案的点,也因为i+1这个点的斜率增加不会更新到i+1;
这样就可以很方便的用单调队列来解决了
其中有几点需要注意的是,
一般每个队列中存的是凸包上点对应的编号,而不是相应的坐标值,因为有可能坐标值是爆了数据范围的;
有的这类题每一个点的最优值是正好卡在longlong之下的,但是中间过程求凸包的时候却有可能爆longlong
解决方法之一是写个几位的高精度(划掉
也可以用double,中间过程求凸包的比较大小用double,虽然有误差,但也不会影响到大小的比较;
最后求一个点dp值的时候,比较方便的做法是套用最基础的公式,用队首的编号所对应的各项值直接算出;
毕竟一般很容易就能证明dp最优值是不会爆数据范围的
接下来放几个我今天水的题~都是公式推推推的没啥好说的
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int maxn=50005;
int que[maxn];
int n;
LL l;
LL dp[maxn];
LL s[maxn];
LL tp;
int head=0,tail=0;
double calc(int x,int y)
{
return ((double)(dp[x]-dp[y])+(double)(s[x]+l)*(double)(s[x]+l)-(double)(s[y]+l)*(double)(s[y]+l))/(double)(s[x]-s[y]);
}
void insert(int i)
{
for(;tail-1>head;tail--)
{
int ta=que[tail-1];
int tb=que[tail-2];
if(calc(i,tb)>calc(ta,tb))break;
}
que[tail++]=i;
}
LL tpv;
LL ans=0;
double cal(int j,int i)
{
return (double)dp[j]+(double)(s[j]+l)*(s[j]+l)-2.0*s[i]*(double)(s[j]+l)+(double)s[i]*s[i];
}
int main()
{
scanf("%d%lld",&n,&l);
l++;
que[tail++]=0;
s[0]=dp[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&tp);
s[i]=s[i-1]+tp+1;
for(head;head<tail-1;head++)
if(cal(que[head],i)<cal(que[head+1],i))break;
int j=que[head];
dp[i]=dp[j]+(s[i]-s[j]-l)*(s[i]-s[j]-l);
insert(i);
}
printf("%lld\n",dp[n]);
return 0;
}
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int maxn=1000005;
LL a,b,c;
LL dp[maxn];
int n;
int que[maxn];
LL sum[maxn];
double X(int i){
return (double)((double)sum[i]*2*a-(double)b);
}
double Y(int i){
return (double)((double)dp[i]+(double)c+(double)(a*sum[i]-b)*sum[i]);
}
double getval(int i,int j)
{
return (double)a*sum[i]*sum[i]-(double)X(j)*sum[i]+(double)Y(j);
}
LL tp;
int tail=0,head=0;
int main()
{
scanf("%d",&n);
scanf("%lld%lld%lld",&a,&b,&c);
sum[0]=dp[0]=0;
que[tail++]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&tp);
sum[i]=sum[i-1]+tp;
for(;head<tail-1;head++)
if(getval(i,que[head])>getval(i,que[head+1]))break;
int j=que[head];
dp[i]=dp[j]+(a*(sum[i]-sum[j])+b)*(sum[i]-sum[j])+c;
for(;tail-1>head;tail--){
int j=que[tail-1];
int k=que[tail-2];
double X1=X(i)-X(j);
double Y1=Y(i)-Y(j);
double X2=X(j)-X(k);
double Y2=Y(j)-Y(k);
if(X1*Y2<Y1*X2)break;
}
que[tail++]=i;
}
printf("%lld\n",dp[n]);
return 0;
}
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define inf 1349880437
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int maxn=3005;
int n,m;
int que[maxn];
int tail,head;
int dp[maxn][maxn];
int sum[maxn];
int a[maxn];
int pow(int i,int j)
{
return (sum[i]-sum[j])*(sum[i]-sum[j]);
}
double X(int i,int k)
{
return (double)sum[i]*k;
}
double Y(int i,int k)
{
return (double)((double)(dp[k-1][i]+(double)pow(i,0))/(double)(k-1)+(double)pow(i,0))*k;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=a[i]+sum[i-1];
}
for(int i=0;i<=m;i++)
for(int j=0;j<=n;j++)dp[i][j]=inf;
dp[0][0]=0;
for(int i=1;i<=n;i++)dp[1][i]=0;
for(int k=2;k<=m;k++)
{
head=tail=0;
for(int i=k;i<=n;i++)
{
int j=i-1;
for(;tail-1>head;tail--){
double XX=X(j,k);
double YY=Y(j,k);
double X1=X(que[tail-1],k);
double Y1=Y(que[tail-1],k);
double X2=X(que[tail-2],k);
double Y2=Y(que[tail-2],k);
if((XX-X1)*(Y1-Y2)-(X1-X2)*(YY-Y1)<0)break;
}
que[tail++]=j;
for(;head<tail-1;head++)
if(Y(que[head],k)-sum[i]*X(que[head],k)*2<Y(que[head+1],k)-sum[i]*X(que[head+1],k)*2)break;
j=que[head];
dp[k][i]=((dp[k-1][j]+pow(j,0))/(k-1)+pow(i,j))*k-pow(i,0);
}
}
printf("%d\n",dp[m][n]);
return 0;
}