真 陈年老题

都是基础的dp优化

主要是展现我基础薄弱,菜得抠脚

1.四边形不等式

四边形不等式:w[i][j]+w[i+1][j+1]<=w[i+1][j]+w[i][j+1]

对于f[i][j]=f[x][k]+f[k+1][y]+w[x][y],若w同时满足区间包含单调性和四边形不等式,那么f也满足四边形不等式

若f满足四边形不等式,具有决策单调性。设f[i][j]的决策点k为s[i][j],s[i+1][j]>=s[i][j]>=s[i][j-1]

证明略。

最最经典的例题,合并石子

求最小值时因为满足四边形不等式,利用s的决策单调性即可n^2

求最大值时不满足四边形不等式,但是f[i][j]=max(f[i+1][j],f[i][j-1])+w[i][j] 也是n^2

llj给的证明(菜得连合并石子都不会的我):对于任意的四堆石子a,b,c,d不存在一种最优的合并方式是a,b合并,c,d合并再合并在一起。

因为从a到d依次合并和从d到a依次合并中一定有一个比他更优。列式可得。

 //Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=;
typedef long long LL;
typedef double db;
using namespace std;
int n,a[N],sum[N],f[N][N],g[N][N],w[N][N],s[N][N]; template<typename T> void read(T &x) {
char ch=getchar(); x=; T f=;
while(ch!='-'&&(ch<''||ch>'')) ch=getchar();
if(ch=='-') f=-,ch=getchar();
for(;ch>=''&&ch<='';ch=getchar()) x=x*+ch-''; x*=f;
} //#define DEBUG
int main() {
#ifdef DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
read(n);
For(i,,n) { read(a[i]); sum[i]=sum[i-]+a[i]; }
For(i,,n) { a[n+i]=a[i]; sum[n+i]=sum[n+i-]+a[n+i]; }
n*=;
For(i,,n) For(j,,n) w[i][j]=sum[j]-sum[i-];
Rep(i,n,) For(j,i+,n) g[i][j]=max(g[i+][j],g[i][j-])+w[i][j];
memset(f,/,sizeof(f));
For(i,,n) f[i][i]=;
Rep(i,n,) {
s[i][i]=i;
For(j,i+,n)
For(k,s[i][j-],s[i+][j]) {
if(k>=i&&k<j&&f[i][k]+f[k+][j]+w[i][j]<f[i][j]) {
f[i][j]=f[i][k]+f[k+][j]+w[i][j];
s[i][j]=k;
}
}
}
n/=;
int ans1=f[][n],ans2=g[][n];
For(i,,n) ans1=min(ans1,f[i][i+n-]),ans2=max(ans2,g[i][i+n-]);
printf("%d\n%d\n",ans1,ans2);
return ;
}

2.决策单调性和斜率优化

非常非常经典的例题,玩具装箱

列出dp方程:
$f_i=min_{j=0}^{j<i}  f[j]+(i-j-1-L+sum[i]-sum[j])^2$

1.
易证代价函数满足四边形不等式,或打表发现,具有决策单调性
维护单调队列,新进入一个决策点弹出队尾的一部分决策点然后在队尾二分即可。
更新答案的时候弹出队首的部分用完的决策点
注意决策点不一定入队。

2.

$x_j=sum_j+j$
$y_j=x_j^2+f_j$
$A_i=i+sum[i]-1-L$
$f_i=A_i^2-2*A_i*x_j+y_j$
若j优于k,则有
$-2*A_i*x_j+y_j<-2*A_i*x_k+y_k$

决策单调性,$j>k,x_j>x_k,y_j>y_k$

$(y_j-y_k)/(x_j-x_k)<2*A_i$

$上式为j,k(j>k) 两点的斜率,维护单调队列,队列中斜率单增即可$

A单增,每次弹出队首斜率小于A的部分,剩下的第一个即为答案

若加入新点i,使kj,ji斜率下降,对于每个A,若kj斜率大于A,则j不如k优,否则ji斜率一定小于A,j不如i优,那么一定可以弹出j,故维护斜率单增的队列是合法的。

 //Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=;
typedef long long LL;
typedef double db;
using namespace std;
int n,ql,qr;
LL L,sum[N],c[N],f[N]; template<typename T> void read(T &x) {
char ch=getchar(); x=; T f=;
while(ch!='-'&&(ch<''||ch>'')) ch=getchar();
if(ch=='-') f=-,ch=getchar();
for(;ch>=''&&ch<='';ch=getchar()) x=x*+ch-''; x*=f;
} struct node {
int x,pos;
node(){}
node(int x,int pos):x(x),pos(pos){}
}que[N]; LL pf(LL x) { return x*x; }
int ck(int i,int j,int pos) {
return f[i]+pf((LL)pos-i--L+sum[pos]-sum[i])<=f[j]+pf((LL)pos-j--L+sum[pos]-sum[j]);
} //#define DEBUG
int main() {
#ifdef DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
read(n); read(L);
For(i,,n) { read(c[i]); sum[i]=sum[i-]+c[i]; }
ql=; qr=;
que[ql]=node(,);
For(i,,n) {
while(ql<qr&&que[ql+].pos-<i) ql++;
int j=que[ql].x;
f[i]=(f[j]+pf((LL)i-j--L+sum[i]-sum[j]));
while(ql<=qr&&ck(i,que[qr].x,que[qr].pos)) qr--;
if(ql>qr) que[++qr]=node(i,);
else {
int l=que[qr].pos,r=n,pos=-,j=que[qr].x;
while(l<=r) {
int mid=((l+r)>>);
if(ck(i,j,mid)) pos=mid,r=mid-;
else l=mid+;
}
if(pos!=-) que[++qr]=node(i,pos);
}
}
printf("%lld\n",f[n]);
return ;
}

决策单调性

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define eps 1e-10
const int N=;
typedef long long LL;
typedef double db;
using namespace std;
int n,ql,qr,que[N];
LL L,sum[N],c[N],f[N];
db x[N],y[N]; template<typename T> void read(T &x) {
char ch=getchar(); x=; T f=;
while(ch!='-'&&(ch<''||ch>'')) ch=getchar();
if(ch=='-') f=-,ch=getchar();
for(;ch>=''&&ch<='';ch=getchar()) x=x*+ch-''; x*=f;
} db pf(db x) { return x*x; }
db get_xl(int a,int b) {
return (y[a]-y[b])/(x[a]-x[b]);
}
LL calc(int j,int i) { return f[j]+pf((LL)i-j--L+sum[i]-sum[j]); } //#define DEBUG
int main() {
#ifdef DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
read(n); read(L);
For(i,,n) { read(c[i]); sum[i]=sum[i-]+c[i]; }
ql=; que[qr=]=;
For(i,,n) {
db A=i+sum[i]--L;
while(ql<qr&&get_xl(que[ql+],que[ql])<A*2.0) ql++;
f[i]=calc(que[ql],i);
x[i]=sum[i]+i;
y[i]=pf(sum[i]+i)+f[i];
while(ql<qr&&get_xl(i,que[qr])+eps<get_xl(que[qr],que[qr-])) qr--;
que[++qr]=i;
}
printf("%lld\n",f[n]);
return ;
}

斜率优化

3.凸包优化

对于f[i]=x[j]*a[i]+y[j]*b[i]这样的转移方程

若决策直线斜率满足单调性则可以做到O(n)

否则需要用数据结构维护凸包,

平衡树维护凸包的模板题货币兑换

第j天能购买的A,B券

$A:x=(Rt_k*f_j)/(Rt_k*A_k+B_k)$

$B:y=f_j/(Rt_k*A_k+B_k)$

$f_i=MAX(x*B_i+y*A_i)$

设$t_i=max_{j<=i}(f_j)$

$w_k=Rt_k*A_k+B_k$

$f_i=MAX(t_k*(B_i/W_k+Rt_k*A_i/W_k))$

$x_i=t_i/W_i,y_i=x_i*Rt_i$

$f_i=MAX(x_k*B_i+y_k*A_i)$

$设p=x_k*B_i+y_k*A_i$

$y_k=-B_i/A_i*x_k+p/A_i$

A>0,p最大即纵截距最大,用splay维护上凸壳即可

一开始不知道怎么着脑抽硬要维护右上凸壳,有猫病吧我

码力是真的弱,主要就是很多地方没有想清楚就开敲,到底什么时候需要弹什么点
然后代码要写得具有可读性,写太丑是真的自己都看不下去,bug根本de不出来

错误示范:80分的丑陋的代码

 int y=pre(i),z=nxt(i);
//if(!z&&y&&dcmp((q[i].y-q[y].y)/(q[i].x-q[y].x))>=0) { del(i); continue; }
//else
if(!y&&z&&dcmp((q[z].y-q[i].y)/(q[z].x-q[i].x))>=0) { del(i); continue; }
else if(y&&z&&dcmp(cross(q[z]-q[y],q[i]-q[y]))<=0) { del(i); continue; }
for(;;) {
y=pre(i); if(!y) break;
db k=(q[i].y-q[y].y)/(q[i].x-q[y].x);
if(!q[y].slop||(dcmp(k)<0&&dcmp(q[y].slop-k)>0)) break;
del(y);
}
j=pre(i);
if(!j) q[i].slop=0,q[i].lz=1;
else q[i].slop=(q[i].y-q[j].y)/(q[i].x-q[j].x);
for(;;) {
y=nxt(i); if(!y) break;
db k=(q[y].y-q[i].y)/(q[y].x-q[i].x);
if((dcmp(k)<0)&&dcmp(q[i].slop-k)>0) break;
del(y);
}
y=nxt(i); if(y) q[y].lz=0,q[y].slop=(q[y].y-q[i].y)/(q[y].x-q[i].x);

正确示范:优美的代码更不容易出错

void check(int x) {
int y=pre(x),z=nxt(x);
int k=dcmp(cross(q[z]-q[y],q[x]-q[y]));
if(y&&z&&k<=0) { del(x); return; }
for(;;) {
y=pre(x);
if(!y) { q[x].lslop=inf; break; }
db k=get_slop(q[x],q[y]);
if(dcmp(k-q[y].lslop)<0) {
q[y].rslop=q[x].lslop=k; break;
} del(y);
}
for(;;) {
z=nxt(x);
if(!z) { q[x].rslop=-inf; break; }
db k=get_slop(q[z],q[x]);
if(dcmp(k-q[z].rslop)>0) {
q[z].lslop=q[x].rslop=k; break;
} del(z);
}
}

完整代码:

 //Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define inf 1e9
const int N=;
typedef long long LL;
typedef double db;
using namespace std;
int n;
db f[N],s,A[N],B[N],Rt[N],t[N],mx; template<typename T> void read(T &x) {
char ch=getchar(); x=; T f=;
while(ch!='-'&&(ch<''||ch>'')) ch=getchar();
if(ch=='-') f=-,ch=getchar();
for(;ch>=''&&ch<='';ch=getchar()) x=x*+ch-''; x*=f;
} struct pt {
db x,y,lslop,rslop;
pt(){}
pt(db x,db y):x(x),y(y){}
}q[N];
#define eps 1e-10
pt operator -(const pt&A,const pt&B) { return pt(A.x-B.x,A.y-B.y); }
int dcmp(db x) { if(fabs(x)<=eps) return ; return x>?:-; }
db cross(pt a,pt b) { return a.x*b.y-a.y*b.x; } int p[N],ch[N][],rt;
#define lc ch[x][0]
#define rc ch[x][1]
void rotate(int x) {
int y=p[x],z=p[y],l=(x==ch[y][]),r=(l^);
if(z) ch[z][y==ch[z][]]=x; p[x]=z;
ch[y][l]=ch[x][r]; p[ch[x][r]]=y;
ch[x][r]=y; p[y]=x;
} void splay(int x,int FA) {
for(;p[x]!=FA;rotate(x)) {
int y=p[x],z=p[y];
if(z!=FA) ((x==ch[y][])^(y==ch[z][]))?rotate(x):rotate(y);
} if(!FA) rt=x;
} void insert(int id) {
int x=rt,f=,l=;
for(;;) {
if(!x) {
p[id]=f; if(f) ch[f][l]=id; else rt=x;
splay(id,); break;
}
if(dcmp(q[x].x-q[id].x)>) f=x,x=lc,l=;
else f=x,x=rc,l=;
}
} int pre(int x) {
splay(x,); x=lc;
while(rc) x=rc;
return x;
} int nxt(int x) {
splay(x,); x=rc;
while(lc) x=lc;
return x;
} void del(int x) {
if(!lc) {
if(x==rt) rt=rc,p[rt]=;
else ch[p[x]][x==ch[p[x]][]]=rc,p[rc]=p[x];
}
else if(!rc) {
if(x==rt) rt=lc,p[rt]=;
else ch[p[x]][x==ch[p[x]][]]=lc,p[lc]=p[x];
}
else {
int y=pre(x);
int z=nxt(x);
splay(y,);
splay(z,y);
p[ch[z][]]=ch[z][]=;
}
} int find(db k) {
for(int x=rt;x;) {
int t1=dcmp(q[x].lslop-k),t2=dcmp(q[x].rslop-k);
if(t1>=&&t2<=) return x;
if(t1<) x=lc;
else x=rc;
} return -;
} db get_slop(pt A,pt B) { return (A.y-B.y)/(A.x-B.x); } void check(int x) {
int y=pre(x),z=nxt(x);
int k=dcmp(cross(q[z]-q[y],q[x]-q[y]));
if(y&&z&&k<=) { del(x); return; }
for(;;) {
y=pre(x);
if(!y) { q[x].lslop=inf; break; }
db k=get_slop(q[x],q[y]);
if(dcmp(k-q[y].lslop)<) {
q[y].rslop=q[x].lslop=k; break;
} del(y);
}
for(;;) {
z=nxt(x);
if(!z) { q[x].rslop=-inf; break; }
db k=get_slop(q[z],q[x]);
if(dcmp(k-q[z].rslop)>) {
q[z].lslop=q[x].rslop=k; break;
} del(z);
}
} //#define DEBUG
int main() {
#ifdef DEBUG
freopen("1.in","r",stdin);
freopen("my.out","w",stdout);
#endif
scanf("%d%lf",&n,&s);
For(i,,n) {
scanf("%lf%lf%lf",&A[i],&B[i],&Rt[i]);
int j=find(-B[i]/A[i]);
if(i!=) f[i]=q[j].x*B[i]+q[j].y*A[i];
else f[i]=s; f[i]=max(f[i],f[i-]);
mx=max(mx,f[i]);
q[i].x=mx/(Rt[i]*A[i]+B[i]); q[i].y=q[i].x*Rt[i];
insert(i); check(i);
}
db ans=;
printf("%.3lf\n",f[n]);
return ;
}

4.多重背包的nm做法

noip之前在长沙学的,印象不是很深,所以拿出来

$f_i=MAX(\sum_{k=0}^{min(i/w,c)}f[i-k*w]+k*val)$

那么每次按模w分类,每一类可以用单调队列维护

 //Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=;
typedef long long LL;
typedef double db;
using namespace std;
int n,W,w[N],v[N],c[N],que[N],ql,qr;
LL f[][N],ans; template<typename T> void read(T &x) {
char ch=getchar(); x=; T f=;
while(ch!='-'&&(ch<''||ch>'')) ch=getchar();
if(ch=='-') f=-,ch=getchar();
for(;ch>=''&&ch<='';ch=getchar()) x=x*+ch-''; x*=f;
} //#define DEBUG
int main() {
#ifdef DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
read(n); read(W);
For(i,,n) {
read(w[i]); read(v[i]); read(c[i]);
}
int o=;
For(i,,n) {
o^=;
memset(f[o],,sizeof(f[o]));
For(d,,w[i]-) {
ql=; qr=;
for(int j=;j*w[i]+d<=W;j++) {
while(ql<=qr&&j-que[ql]>c[i]) ql++;
f[o][j*w[i]+d]=f[o^][j*w[i]+d];
if(ql<=qr)
f[o][j*w[i]+d]=max(f[o][j*w[i]+d],f[o^][que[ql]*w[i]+d]+(j-que[ql])*v[i]);
while(ql<=qr&&f[o^][que[qr]*w[i]+d]-que[qr]*v[i]<=f[o^][j*w[i]+d]-j*v[i]) qr--;
que[++qr]=j;
}
}
}
printf("%lld\n",f[o][W]);
return ;
}

几个dp的陈年老题的更多相关文章

  1. 63. Unique Paths II(中等, 能独立做出来的DP类第二个题^^)

    Follow up for "Unique Paths": Now consider if some obstacles are added to the grids. How m ...

  2. 思维题练习专场-DP篇(附题表)

    转载请注明原文地址http://www.cnblogs.com/LadyLex/p/8536399.html 听说今年省选很可怕?刷题刷题刷题 省选已经结束了但是我们要继续刷题刷题刷题 目标是“有思维 ...

  3. HDU 1176 免费馅饼 DP类似数塔题

    解题报告: 小明走在一条小路上,这条小路的长度是10米,从左到右依次是0到10一共十个点,现在天上会掉馅饼,给出馅饼掉落的坐标和时间,一开始小明的位置是在坐标为5的位置, 他每秒钟只能移动一米的距离, ...

  4. codeforces 1101F Trucks and Cities 区间dp+单调优化 好题

    题目传送门 题意简述:(来自洛谷) 有n个城市坐落在一条数轴上,第ii个城市位于位置ai​. 城市之间有m辆卡车穿行.每辆卡车有四个参数:si​为起点编号,fi​为终点编号,ci​表示每行驶1个单位长 ...

  5. 「洛谷5017」「NOIP2018」摆渡车【DP,经典好题】

    前言 在考场被这个题搞自闭了,那个时候自己是真的太菜了.qwq 现在水平稍微高了一点,就过来切一下这一道\(DP\)经典好题. 附加一个题目链接:[洛谷] 正文 虽然题目非常的简短,但是解法有很多. ...

  6. BZOJ 2734 洛谷 3226 [HNOI2012]集合选数【状压DP】【思维题】

    [题解] 思维题,看了别人的博客才会写. 写出这样的矩阵: 1,3,9,... 2,6,18,... 4,12.36,... 8,24,72,... 我们要做的就是从矩阵中选出一些数字,但是不能选相邻 ...

  7. bzoj5210最大连通子块和 (动态dp+卡常好题)

    卡了一晚上,经历了被卡空间,被卡T,被卡数组等一堆惨惨的事情之后,终于在各位大爹的帮助下过了这个题qwqqq (全网都没有用矩阵转移的动态dp,让我很慌张) 首先,我们先考虑一个比较基础的\(dp\) ...

  8. ZOJ 3201 树形dp+背包(简单题)

    #include<cstdio> #include<vector> #include<cstring> #include<iostream> using ...

  9. 借助树的概率dp(期望)+数学-好题-hdu-4035-Maze

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4035 题目意思: 有n个房间,有n-1条通道连接这n个房间(每两个房间之间有且只有一条路,所以实际上 ...

随机推荐

  1. linux秘钥登录

    秘钥登录首先要了解四个文件: 公钥文件,私钥文件, authorized_keys, 还有/etc/ssh/sshd_config配置文件. 公钥文件存放在被登陆的机器上, 要将公钥添加进author ...

  2. java拷贝--clone

    大纲: java如何拷贝对象. 浅拷贝 深拷贝 一.java如何拷贝对象 Person p = new Person(); Person p2 = p; 上例并不是一个拷贝操作,只是把p对象的引用赋给 ...

  3. 8-26接口压力测试-1Dubbo接口测试

    1. Dubbo Dubbo是一个分布式服务框架,提供了高性能和透明化的RPC(Remote Procedure Call Protocol)远程服务调用方案和服务治理方案. SOA:面向服务的架构 ...

  4. ASCII 对应表

    { Bin (二进制) Oct (八进制) Dec (十进制) Hex (十六进制) 缩写/字符 解释 0000 0000 00 0 0x00 NUL(null) 空字符 0000 0001 01 1 ...

  5. CRI 与 ShimV2:一种 Kubernetes 集成容器运行时的新思路

    摘要: 关于 Kubernetes 接口化设计.CRI.容器运行时.shimv2.RuntimeClass 等关键技术特性的设计与实现.     Kubernetes 项目目前的重点发展方向,是为开发 ...

  6. thinkphp 域名部署

    ThinkPHP支持模块(甚至可以包含控制器)的完整域名.子域名和IP部署功能,让你的模块变得更加灵活,模块绑定到域名或者IP后,URL地址中的模块名称就可以省略了,所以还可以起到简化URL的作用. ...

  7. Nginx被动健康检查和主动健康检查

    1.被动健康检查 Nginx自带有健康检查模块:ngx_http_upstream_module,可以做到基本的健康检查,配置如下: upstream cluster{ server max_fail ...

  8. APIO 2007 风铃

    题目描述 你准备给弟弟 Ike 买一件礼物,但是,Ike 挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西. 你准备给 Ike 买一个风铃.风铃是一种多层的装饰品,一般挂在天花板上. 每个风 ...

  9. NX二次开发-UFUN获取当前导出CGM选项设置UF_CGM_ask_session_export_options

    文章转载自唐康林NX二次开发论坛,原文出处: http://www.nxopen.cn/thread-126-1-1.html 刚才有同学问到这个问题,如果是用NXOpen来做,直接录制一下就可以了: ...

  10. three.js-走进3d的奇妙世界一创建一个三维场景

      一.git代码仓库地址 git clone https://github.com/josdirksen/learning-threejs-third  下载并解压 二.创建一个三维场景 如下图所示 ...