bzoj 1010 [HNOI2008] 玩具装箱toy (斜率优化第一题)
题意:中文题,大意就不说了,但这个Sigma的确让我看了好久
思路:看了网上的很多题解,大概对斜率优化有了一点了解,发现斜率优化这种东西用在很多方面,比如在对凸壳的求解中,常用的kuangbin凸包模板中就有斜率优化的例子,对于单调队列的认识是基于某次被学弟们踩的一次训练(bzoj 1012 [JSOI2008] 最大数maxnumber,这个题用点掉队列的解法的确很巧妙,orz.),而斜率优化,与点掉队列优化类似,队列优化解决的是在dp方程中,经过一些变化后,可以将dp方程两侧化为dp[i]和dp[j],而无dp[i]*dp[j]类似的项,就是不牵扯高次幂项,而写斜率优化正是将相乘的两项转化为一种特殊的函数,转变成在二维平面上的一些点,在决策性的讨论上,根据所变形得到的方程,来决定维护上凸壳还是下凸壳。
首先附上超时代码O(n2);
加个字符读入板子:
inline char nc() { static char ibuf[RLEN],*ib,*ob; (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin)); return (ib==ob) ? -1 : *ib++; }
#include <bits/stdc++.h> using namespace std; const int maxn=50005; const int INF=0x3f3f3f3f; typedef long long LL; LL pre[maxn],dp[maxn]; int a[maxn]; int n,l; int main() { scanf("%d%d",&n,&l); pre[0]=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); pre[i]=pre[i-1]+a[i]; } memset(dp,0x3f,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++){ int j; int as; for(j=0;j<i;j++){ as=i-j-1+pre[i]-pre[j]-l; dp[i]=min(dp[i],dp[j]+as*as); } } printf("%lld\n",dp[n]); return 0; }
然后抄袭hzw大牛的代码:
#include <bits/stdc++.h> const int inf=1000000000; typedef long long ll; using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,L,l,r; int c[50005],q[50005]; ll s[50005],f[50005],C; double slop(int j,int k) { return (f[k]-f[j]+(s[k]+C)*(s[k]+C)-(s[j]+C)*(s[j]+C))/(2.0*(s[k]-s[j])); } int main() { n=read();L=read();C=L+1; for(int i=1;i<=n;i++)c[i]=read(); for(int i=1;i<=n;i++)s[i]=s[i-1]+c[i]; for(int i=1;i<=n;i++)s[i]+=i;//这里是因为在dp方程中吧前缀和与i进行合并了 l=1;r=0;q[++r]=0; for(int i=1;i<=n;i++){ while(l<r&&slop(q[l],q[l+1])<=s[i])l++; int t=q[l];//取出队首 f[i]=f[t]+(s[i]-s[t]-C)*(s[i]-s[t]-C);//这里其实是dp方程 while(l<r&&slop(q[r],i)<slop(q[r-1],q[r]))r--;//由斜率方程可以得出,维护的是下凸壳,所以当队列存在且 q[++r]=i; //加入决策i时,令队尾为q[r],前一个为q[r-1] //满足斜率(q[r],i)<斜率(q[r-1],q[r])时,显然队尾是无效的,将其弹出 } printf("%lld\n",f[n]); return 0; }