真 陈年老题

都是基础的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. android webview 输入法键盘遮挡输入框的问题

    新建一个工具类: /** * 解决webView键盘遮挡问题的类 * Created by zqy on 2016/11/14. */ public class KeyBoardListener { ...

  2. md详解和rd详解:一次性创建多个目录和多级子目录

    md 命令: 官方解释: E:\ABC>md /? 创建目录. MKDIR [drive:]path MD [drive:]path 如果命令扩展被启用,MKDIR 会如下改变: 如果需要,MK ...

  3. 从零开始搭搭建系统3.1——顶级pom制定

    从零开始搭搭建系统3.1——顶级pom制定

  4. [转]Netty入门(最简单的Netty客户端/服务器程序)

    Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...

  5. leetcood学习笔记-501- 二叉搜索树中的众数

    题目描述: 方法一: class Solution: def findMode(self, root: TreeNode) -> List[int]: if not root: return [ ...

  6. QT install

    { https://www.bilibili.com/video/av18148008?from=search&seid=15361598961528715331 }

  7. JavaWeb学习篇之----web应用的虚拟目录映射和主机搭建(Tomcat)

    从今天开始来学习JavaWeb的相关知识,之前弄过一段时间JavaWeb的,就是在做毕业设计的时候搞过,但是那时候完全是为了任务去学习,所以效果不好,好多东西都没有深入的研究过,所以接下来的一段时间我 ...

  8. error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead.【转载】

    文章出处https://blog.csdn.net/qq_38721302/article/details/82850292 今天编写C++程序在使用头文件#include<cstring> ...

  9. ashx后门[转]

    https://www.cnblogs.com/Fluorescence-tjy/p/9855828.html 一.标准ASPX一句话木马 .NET平台下的一句话木马则百年不变,最常见的当属下面这句 ...

  10. Spring 源码学习——注册 BeanDefinition

    BeanFactory BeanFactory 是 Spring IoC 容器的具体实现,是 Spring 容器的核心接口. DefaultListableBeanFactory XmlBeanFac ...