2024.5.21
斜率优化 dp 模板题,一共花了 3 days 才 AC。
由于我个人习惯而且写单调队列的边界一直写的都是初始化 l=1,r=0,访问条件 l<=r。
按照这种写法,得到了一份 WA 的代码。
结果发现我的队列没有清空。经过清空后得到的 70pts 代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=5e5+100;
typedef long long ll;
int n,m,q[N],l,r;
ll a[N],dp[N];
ll get_x(ll x)
{
return 2*a[x];
}
ll get_y(ll x)
{
return dp[x]+a[x]*a[x];
}
ll slope_fz(ll x,ll y)
{
return get_y(x)-get_y(y);
}
ll slope_fm(ll x,ll y)
{
return get_x(x)-get_x(y);
}
int main()
{
//freopen("sj.in","r",stdin);
//freopen("4838.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
dp[0]=q[0]=0,l=1,r=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]+=a[i-1],q[i]=0;
}
for(int i=1;i<=n;i++)
{
while(l<=r&&slope_fz(q[l+1],q[l])<=a[i]*slope_fm(q[l+1],q[l])) l++;
dp[i]=dp[q[l]]+m+(a[i]-a[q[l]])*(a[i]-a[q[l]]);
while(l<=r&&slope_fz(i,q[r])*slope_fm(q[r],q[r-1])<=slope_fz(q[r],q[r-1])*slope_fm(i,q[r])) r--;
q[++r]=i;
}
printf("%lld\n",dp[n]);
}
return 0;
}
/*
10 773
116
1
75
147
80
101
97
15
35
39
*/
多测要清空。
于是拿算法竞赛的代码进行一个比的对,发现区别只有队列的边界。但是我认为我的边界写法没有任何问题,于是与正解进行对拍,得到了一组错误的小数据。
经过 2 days 的推导后找出了问题所在。
正解如此处理边界的原因是要放一个 \(0\) 在队头,在第一个点入队时计算出的是 \(0\) 与它的斜率。如果按照我的方法处理则是计算出第一个和 \(0\) 的斜率。相当于把斜率的计算方式从 \(\frac{y_1-y_2}{x_1-x_2}\) 变为了 \(\frac{x_1-x_2}{y_1-y_2}\),因此会出现错误。
100pts 代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=5e5+100;
typedef long long ll;
int n,m,q[N],l,r;
ll a[N],dp[N];
ll get_x(ll x)
{
return 2*a[x];
}
ll get_y(ll x)
{
return dp[x]+a[x]*a[x];
}
ll slope_fz(ll x,ll y)
{
return get_y(x)-get_y(y);
}
ll slope_fm(ll x,ll y)
{
return get_x(x)-get_x(y);
}
int main()
{
//freopen("sj.in","r",stdin);
//freopen("4838.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
dp[0]=a[0]=q[0]=0,l=1,r=1;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]+=a[i-1],q[i]=0;
}
for(int i=1;i<=n;i++)
{
while(l<r&&slope_fz(q[l+1],q[l])<=a[i]*slope_fm(q[l+1],q[l]))
{
//printf("%d %d\n",slope_fz(q[l+1],q[l]),a[i]*slope_fm(q[l+1],q[l]));
//printf("%d %d\n",q[l+1],q[l]);
l++;
}
dp[i]=dp[q[l]]+m+(a[i]-a[q[l]])*(a[i]-a[q[l]]);
while(l<r&&slope_fz(i,q[r])*slope_fm(q[r],q[r-1])<=slope_fz(q[r],q[r-1])*slope_fm(i,q[r])) r--;
q[++r]=i;
//printf("%d ",dp[i]);
}
printf("%lld\n",dp[n]);
}
return 0;
}
/*
10 773
116
1
75
147
80
101
97
15
35
39
76779
*/
总结:多测清空+单调队列边界处理。

浙公网安备 33010602011771号