再学习一下动态规划的基本优化方法…

首先这篇文章应该大家都看过吧…没看过的自行百度

关于实现的思路文章里都给好了…这篇就主要给一点题目啥的

(P.S. 电脑重装了,如果博客发出来有一些奇怪的问题不要在意)

模型一,即决策单调性优化

①玩具装箱 bzoj1010

题目自己看去

用dp[x]表示装前x个的最小费用,sum[x]表示C的前缀和。

可以发现dp[i]=min{dp[j]+(i-j+sum[i]-sum[j]-1-L)^2} (0<=j<i)

这样似乎还是不够美观,我们令p[i]=i+sum[i],g=L+1。

dp[i]=min{dp[j]+(p[i]-p[j]-g)^2} (0<=j<i)

美观了一点…

这样硬肛是O(n^2)的,感觉很不兹磁啊…

首先:四边形不等式

当函数w(i,j)满足w(a,c)+w(b,d)<=w(b,c)+w(a,d)且a<=b<c<=d时我们称w(i,j)满足四边形不等式。

假如我们用w(j,i)表示(p[i]-p[j]-g)^2,那么(打表)可以发现w是满足四边形不等式的。

什么?讲道理?

w(a,c)+w(b,d)-w(b,c)-w(a,d)=2g(p[c]-p[a]+p[d]-p[b]-p[c]+p[b]-p[d]+p[a])+p[a]^2+p[c]^2+p[b]^2+p[d]^2-p[b]^2-p[c]^2-p[a]^2-p[d]^2-2p[a]p[c]-2p[b]p[d]+2p[b]p[c]+2p[a]p[d](展开)=-2p[a]p[c]-2p[b]p[d]+2p[b]p[c]+2p[a]p[d](容易观察出剩下的都为0)=2(p[d]-p[c])(p[a]-p[b])<0(由于p显然单调增)。

模型一 且w满足四边形不等式

由于决策单调性,我们可以知道如果a<b<c<d且在c处选a比选b优,那么在d处选a也比选b优。

所以我们可以用一个单调队列(单调栈)来维护决策,每有一个新决策我们就用二分维护队列最优性,队列每一个元素维护对于哪些状态(一定是一个连续的区间)当前这个决策时最优的,然后队列从尾到头一个一个元素二分当前状态哪里最优,然后暴力弹出就行。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
typedef long long ll;
#define SZ 233333
int n,l; ll p[SZ],dp[SZ];
int sn=0,sa[SZ],sl[SZ],sr[SZ];
ll wd(int j,int i) {return dp[j]+(p[i]-p[j]-l)*(p[i]-p[j]-l);}
int main()
{
scanf("%d%d",&n,&l); ++l;
ll sum=0;
for(int i=1;i<=n;i++)
{
int c; scanf("%d",&c);
sum+=c; p[i]=sum+i;
}
sn=1; sa[1]=0; sl[1]=1; sr[1]=n;
for(int i=1;i<=n;i++)
{
int jc=sa[upper_bound(sl+1,sl+1+sn,i)-sl-1];
dp[i]=wd(jc,i);
while(sn&&wd(sa[sn],sl[sn])>=wd(i,sl[sn])) --sn;
if(!sn) {sn=1; sa[1]=i; sl[1]=1; sr[1]=n; continue;}
int l=sl[sn],r=sr[sn]; //到l为止原决策更优
while(l<r)
{
int mid=l+r+1>>1;
if(wd(sa[sn],mid)<wd(i,mid)) l=mid; else r=mid-1;
}
if(l==n) continue;
sr[sn]=l; ++sn; sa[sn]=i;
sl[sn]=l+1; sr[sn]=n;
}
printf("%lld\n",dp[n]);
}

愉快的O(nlogn)。

②土地购买 bzoj1597

购买一些土地,可以分组购买,每组土地的价格是最大长*最大宽,求最小费用。

例如1*5和5*1两块地显然要分2组购买。

我们考虑把长度从高到低排个序,这样我们就只要考虑宽度就行啦。

我们用l表示长度,w表示宽度好了,那么

我们发现这个玩意儿根本不满足决策单调性…

我们回想一下之前做的某一题(其实我也忘了是哪一题),只要把完全没有用,会被完全包含的点删掉就是单调的了。

我们发现如果点(l,w)存在另一个点(l',w')且l<=l',w<=w',那么(l,w)一定可以在选(l',w')的时候被顺便选掉,不会影响总代价。

我们考虑先将l排好序后,用一个单调栈一样的东西来维护一下w,显然是线性的。

之后的方程就是单调的了。

(这里我们改用x表示长度,y表示宽度,懒得改公式了)

还是可以像上一题一样讲道理!

我们令w(k,n)为x[n]*y[k+1],那么

w(a,c)+w(b,d)-w(b,c)-w(a,d)=(x[b]-x[a])*(y[d+1]-y[c+1])。

由于x不减y不增显然<=0。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define X first
#define Y second
#define SZ 666666
typedef pair<int,int> pii;
typedef long long ll;
pii ps[SZ];
int n,sn=0,ss[SZ];
bool dd[SZ];
ll dp[SZ];
int jn=0,jl[SZ],jr[SZ],jc[SZ],nxt[SZ];
ll w(int a,int b) {return ps[b].X*(ll)ps[nxt[a]].Y+dp[a];}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&ps[i].X,&ps[i].Y);
sort(ps+1,ps+1+n);
for(int i=1;i<=n;i++)
{
while(sn&&ps[ss[sn]].Y<=ps[i].Y) dd[ss[sn--]]=1;
ss[++sn]=i;
}
jn=1; jc[1]=0; jl[1]=1; jr[1]=n;
int lst=0;
for(int i=1;i<=n;i++)
{
if(!dd[i]) nxt[lst]=i, lst=i;
}
for(int i=1;i<=n;i++)
{
if(dd[i]) continue;
int t=upper_bound(jl+1,jl+1+jn,i)-jl-1;
dp[i]=w(jc[t],i);
while(jn&&w(jc[jn],jl[jn])>=w(i,jl[jn])) --jn;
if(!jn) {jn=1; jc[1]=i; jl[1]=1; jr[1]=n; continue;}
int l=jl[jn],r=jr[jn];
while(l<r)
{
int mid=l+r+1>>1;
if(w(jc[jn],mid)<w(i,mid)) l=mid; else r=mid-1;
}
if(l==n) continue;
jr[jn]=l; ++jn; jc[jn]=i;
jl[jn]=l+1; jr[jn]=n;
}
printf("%lld\n",dp[n]);
}

模型二,即单调队列优化

这个最典型的就是有限背包(多重背包)

比如我们有若干个物品,价值为v[i],重量为w[i],数量限制为s[i]。

我们考虑一个傻逼dp:

f[i][j]=max{f[i-1][j-x*w[i]]+x*v[i]} (0<=x<=s[i],x*w[i]<=j)

然后我们发现这个x*w[i]<=j不太和谐…

我们考虑把j对模w[i]的余数进行分类!

设j=q*w[i]+p,那么

f[i][p][q]=max{f[i-1][p][x]+(q-x)*v[i]} (0<=x<=q且x>=q-s[i])

所以我们发现可以直接上单调队列优化!

就是说我们可以用一个单调队列来维护决策,队伍里前面的决策比后面的一定优秀。

每一次我们在队尾加新的决策,然后维护队列的单调性。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
using namespace std;
#define SZ 666666
int n,tw;
typedef long long ll;
ll f[SZ];
void pack01(ll w,ll v)
{
for(int i=tw;i>=w;i--) f[i]=max(f[i],f[i-w]+v);
}
void packfull(ll w,ll v)
{
for(int i=w;i<=tw;i++) f[i]=max(f[i],f[i-w]+v);
}
//强行二进制分解
void packmulti2(ll w,ll v,int c)
{
if(w>tw||c==0) return;
if(c==1) {pack01(w,v); return;}
if(w*c>=tw) {packfull(w,v); return;}
for(int k=1;k<c;k<<=1) pack01(w*k,v*k), c-=k;
pack01(w*c,v*c);
}
int qs[SZ],*qh[SZ],*qt[SZ];
ll nf[SZ];
//据说比较优越的单调队列
void packmulti1(ll w,ll v,int c)
{
if(w>tw||c==0) return;
if(c==1) {pack01(w,v); return;}
if(w*c>=tw) {packfull(w,v); return;}
int pss=tw/w+1,*cur=qs;
for(int i=0;i<w;i++) qh[i]=qt[i]=cur, cur+=pss;
for(int i=0;i<=tw;i++)
{
int bl=i%w; ll cv=f[i]-i/w*v;
while(qh[bl]!=qt[bl]&&*qh[bl]<i-c*w) ++qh[bl];
while(qh[bl]!=qt[bl]&&f[*(qt[bl]-1)]-*(qt[bl]-1)/w*v<cv) --qt[bl];
*(qt[bl]++)=i;
nf[i]=f[*qh[bl]]+(i/w-(*qh[bl])/w)*v;
}
for(int i=0;i<=tw;i++) f[i]=nf[i];
}
int main()
{
scanf("%d%d",&n,&tw);
for(int i=1;i<=n;i++)
{
int w,v,m;
scanf("%d%d%d",&w,&v,&m);
if(m==1) pack01(w,v);
else if(m==-1) packfull(w,v);
else packmulti2(w,v,m);
}
printf("%lld\n",f[tw]);
}

不知道是不是我写狗了…单调队列做法跑的特别慢…一脸懵逼

1D1D动态规划优化初步的更多相关文章

  1. 1D1D动态规划优化

    1D1D动态规划优化 1D/1D 动态规划优化初步所谓1D/1D 动态规划,指的是状态数为O(n),每一个状态决策量为O(n)的动态规划方程.直接求解的时间复杂度为O(n2),但是,绝大多数这样的方程 ...

  2. 【bzoj2216】[Poi2011]Lightning Conductor 1D1D动态规划优化

    Description 已知一个长度为n的序列a1,a2,…,an.对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p – sqrt(abs ...

  3. OI动态规划&&优化 简单学习笔记

    持续更新!! DP的难点主要分为两类,一类以状态设计为难点,一类以转移的优化为难点. DP的类型 序列DP [例题]BZOJ2298 problem a 数位DP 常用来统计或者查找一个区间满足条件的 ...

  4. [置顶] 1D1D动规优化初步

    例题一: 货物运输,大意: 给出N个点的坐标与需要你送过去的钱数(第一个点不需要钱),身上带钱的数目有最大值,由初始在的1点,按顺序经历每个点(中途可以回1点,回去钱就满了),问最小走的路程是多少(最 ...

  5. 《挑战程序设计竞赛》2.3 动态规划-优化递推 POJ1742 3046 3181

    POJ1742 http://poj.org/problem?id=1742 题意 有n种面额的硬币,面额个数分别为Ai.Ci,求最多能搭配出几种不超过m的金额? 思路 据说这是传说中的男人8题呢,对 ...

  6. 【DP】斜率优化初步

    向y总学习了斜率优化,写下这篇blog加深一下理解. 模板题:https://www.acwing.com/problem/content/303/ 分析 因为本篇的重点在于斜率优化,故在此给出状态转 ...

  7. 动态规划优化算法——wqs二分 and 折线优化

    坑先扔着,督促自己以后来补!!!

  8. 队列优化和斜率优化的dp

    可以用队列优化或斜率优化的dp这一类的问题为 1D/1D一类问题 即状态数是O(n),决策数也是O(n) 单调队列优化 我们来看这样一个问题:一个含有n项的数列(n<=2000000),求出每一 ...

  9. DP的优化总结

    一.预备知识 \(tD/eD\) 问题:状态 t 维,决策 e 维.时间复杂度\(O(n^{e+t})\). 四边形不等式: 称代价函数 w 满足凸四边形不等式,当:\(w(a,c)+w(b,d)\l ...

随机推荐

  1. Quartz2D复习(二) --- 手势解锁

    这次支付宝手机客户端升级,把手势解锁那个功能去掉了,引起很多人的抱怨,觉得少了手势解锁的保护,个人信息容易泄漏了... 那么手势解锁功能是怎么是实现的呢,这里使用Quart2D来简单模拟一下, 先看下 ...

  2. java 实现https请求

    java 实现https请求 JSSE是一个SSL和TLS的纯Java实现,通过JSSE可以很容易地编程实现对HTTPS站点的访问.但是,如果该站点的证书未经权威机构的验证,JSSE将拒绝信任该证书从 ...

  3. iOS9 HTTP 通信报错解决方案

    UIWebView *myview = [[UIWebView alloc] initWithFrame:CGRectMake(, , [UIScreen mainScreen].bounds.siz ...

  4. 01_iOS开发需要准备什么?

    本文目录 一. 前言 二.IOS开发准备 前言 相信现在的你已经有了一台安装了Xcode或者搭建好Objective-C的电脑了,由于我自己装了黑苹果,所以以后的内容都会直接从黑苹果上运行的Xcode ...

  5. Android 内容观察者的原理

    拦截短信,比如当发短信的时候,就把短信读取出来,当系统的短信发生变化的时候,大叫一声,把数据发送到公共的消息邮箱里面,我们的应用通过内容观察者观察公共的消息邮箱 获取ContentResolver对象 ...

  6. tomcat部署java servlet的3种方式

    1.将编译好的class文件按照与工程中的package的目录结构一致的文件夹底下 2.将你的servlet封装成 .war(web application archive格式的后缀名) 格式的文档直 ...

  7. 你真的了解UIViewController跳转吗?

    一:UIViewController模态跳转 //展示模态视图 - (void)presentViewController:(UIViewController *)viewControllerToPr ...

  8. iOS通用的MVC模式项目框架MobileProject

    最近项目比较不赶的情况下,决定把一些通用.常用的内容集成在一个项目框架中,意在新项目中可以快速搭建:其实经过几个项目后,总是有一些重复的创建工作,可以使用本项目的内容直接进行开发:采用的是MVC的分层 ...

  9. 【代码笔记】iOS-仿安卓,本页出现多个选择项

    一,效果图. 二,代码. //点击任何处,弹出提示选项 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UIAlert ...

  10. Xcode插件

    Xcode插件-2016-03-10Xcode插件管理神器 —— Alcatraz 说明 Alcatraz是一个管理Xcode开源包的,你可以用它查找安装你想要的插件,模板以及配色方案,而你无需手动克 ...