斜率优化 dp 浅谈+刷题笔记
斜率优化 dp 浅谈+刷题笔记
忽略阶段变量,我们如果有这样的 dp 递推式:
\[f_{i}=\max or \min\{f_{k}+val_{k,i} \}
\]
其中如果 \(val_{k,i}\) 即与 \(i\) 有关也与 \(k\) 有关。
这种方程我们通常可以用斜率优化,把凸包维护出来,在上面二分或者单调队列维护。
有一些题目并没有写题解,但是已经写了代码,干脆就放在下面:
1 P3195 [HNOI2008]玩具装箱
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define int long long
#define ull unsigned long long
#define N 50010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline void write(T x) {
if(x < 0) x=-x,putchar('-');
if(x > 9) write(x / 10);
putchar(x%10+'0');
}
template<typename T> inline void writeln(T x) {
write(x);
puts("");
}
int n,L,c[N],f[N],sum[N];
int q[N],l,r;
inline int squ(int k){
return k*k;
}
inline int comp_y(int k){
return f[k]+squ(k+sum[k]);
}
inline int comp_x(int k){
return k+sum[k];
}
inline int comp_k_2(int k){
return 2*(k-1-L+sum[k]);
}
inline dd comp_k_1(int i1,int i2){
dd x1=comp_x(i1),y1=comp_y(i1);
dd x2=comp_x(i2),y2=comp_y(i2);
return (y1-y2)/(x1-x2);
}
signed main(){
read(n);read(L);
for(int i=1;i<=n;i++){
read(c[i]);sum[i]=sum[i-1]+c[i];
}
q[++r]=0;
for(int i=1;i<=n;i++){
while(l<r-1&&comp_k_1(q[l+1],q[l+2])<comp_k_2(i)) l++;
if(l<r){
int k=q[l+1];
f[i]=f[k]+squ(i-k-1+sum[i]-sum[k]-L);
}
while(l<r-1&&comp_k_1(i,q[r])<comp_k_1(q[r],q[r-1])) r--;
q[++r]=i;
}
printf("%lld",f[n]);
return 0;
}
2 P4360 [CEOI2004]锯木厂选址
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define int long long
#define ull unsigned long long
#define N 10001000
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
inline int Min(int a,int b){
return a<b?a:b;
}
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline void write(T x) {
if(x < 0) x=-x,putchar('-');
if(x > 9) write(x / 10);
putchar(x%10+'0');
}
template<typename T> inline void writeln(T x) {
write(x);
puts("");
}
int n,w[N],f[N],x[N],sumw[N],SM[N],g[N],ans=INF;
int q[N],l,r;
inline int com_x(int k){
return sumw[k];
}
inline int com_y(int k){
return f[k]+SM[k];
}
inline int com_k_1(int k){
return x[k];
}
inline dd com_k_2(int i1,int i2){
dd x1=com_x(i1),y1=com_y(i1);
dd x2=com_x(i2),y2=com_y(i2);
return (y1-y2)/(x1-x2);
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(w[i]);read(x[i+1]);
x[i+1]+=x[i];sumw[i]=sumw[i-1]+w[i];
SM[i]=SM[i-1]+x[i]*w[i];
}
w[n+1]=0;sumw[n+1]=sumw[n];SM[n+1]=SM[n];n++;
for(int i=0;i<=n;i++){
f[i]=sumw[i]*x[i]-SM[i];
}
q[++r]=0;
for(int i=1;i<=n;i++){
while(l<r-1&&com_k_2(q[l+1],q[l+2])<com_k_1(i)) l++;
if(l<r){
int k=q[l+1];
g[i]=f[k]+x[i]*(sumw[i]-sumw[k])+SM[k]+x[n]*(sumw[n]-sumw[i])-SM[n];
}
while(l<r-1&&com_k_2(i,q[r])<com_k_2(q[r],q[r-1])) r--;
q[++r]=i;
ans=Min(ans,g[i]);
}
printf("%lld",ans);
return 0;
// for(int i=1;i<=n;i++) printf("%d\n",f[i]);
}