动态规划的四个姿势

动态规划要学好,姿势一定要骚,在实战的时候,你将你的转移方程按照以下四种姿势搞一发后,一定会是耳目一新,引来萌妹子的注意~~~哈哈!!!

言归正传了!!!

之所以写动态规划优化,是因为常常在动态规划时,你会发现,就算你很轻易地定义出状态,和找到状态转移方程,但是你有可能面临时间限制。动态规划的优化,主要体现在一维上,一维已经很成熟了,也有很多专家研究这个,关于acm的动态规划优化有很多。下面展示几个常用的奇技淫巧。

  • LIS :​

​ : 以 i 号元素为结尾的最长递增子序列长度。

​ : 最长递增子序列长度为 i 的最小元素值。

在求 ​ 时:只要在 g 数组中二分,同时更新 g 数组。

例题一 :

nyoj 720

分析:类比LIS,同样​ 会TLE,将状态定义稍微优化一点, 先按时间排序,​ 前 i 个元素能达到的最优值,下一个状态起始时间,就可以在前面的状态中二分到这个时间点。细节有两点,一是,排序因子,因为二分是起始时间,因此得按照右端点优先排序,二是,二分手法,应该是upper_bound。

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <iostream>
#include <algorithm>


using namespace std;

const int maxn = ;

struct Node {
int l,r,v;
bool operator < (const Node & rhs) const {
if(r==rhs.r) return l < rhs.l;
return r < rhs.r;
}
}nodes[maxn];

int n;

int d[maxn];

int upper_bound(int x,int y,int v) {
int m;
while(x<y) {
m = x + (y-x)/;
if(nodes[m].r<=v) x = m+;
else y = m;
}
return x;
}

int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)!=EOF) {
for(int i = ; i < n; i++)
{
scanf("%d%d%d",&nodes[i].l,&nodes[i].r,&nodes[i].v);
}
sort(nodes,nodes+n);
memset(d,,sizeof(d));
d[] = nodes[].v;
for(int i = ; i < n; i++) {
d[i] = max(d[i-],nodes[i].v);
int k = upper_bound(,i,nodes[i].l);
if(k>&&nodes[k-].r<=nodes[i].l)
d[i] = max(d[i],d[k-]+nodes[i].v);
}
printf("%d\n",d[n-]);

}
return ;
}

还记得括号匹配吗?

Google Code jam 2016 Round3 A;就是一道裸的括号匹配。成功匹配得10分,失败得5分。给定一个序列,求最大得分。因为暂时我电脑连不上Google,原题就不贴了。做法很简单,维护一个栈。

例题二: pku2559

题意:求最大子矩阵面积。

此题是三倍经验题哦,还有一个在51Nod,和一场个人赛GYM上出现过(当时有大佬用KMP搞的);这里利用栈来做到​

分析:无非就是要快速求出一个数字作为最小值(最大值)的左右端点区间。暴力​ 实在受不了,这里利用单调栈一下子就降到​ 。

维护一个按照高度递增的单调栈,当新加入的矩形破坏了单调栈的单调性,说明一件事情,就是栈顶元素的生命周期已经结束了,他作为最小值的左右端点已经确定。


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <iostream>
#include <algorithm>

using namespace std;


const int maxn = 1e5+;
int h[maxn];

int main()
{
//freopen("in.txt","r",stdin);
int n;
while(scanf("%d",&n),n) {

for(int i = ; i <= n; i++) scanf("%d",&h[i]);

stack<int> S;
S.push();h[++n] = ;
long long ans = ;
for(int i = ; i <= n; i++) {
while(h[i]<h[S.top()]) {
long long a = h[S.top()];
S.pop();
long long b = i - S.top() - ;
if(ans < a*b) ans = a*b;
}
S.push(i);
}

printf("%lld\n",ans);
}
return ;
}

例题三:pku 2823

题意:滑动窗口最小值(最大值)

分析:如果你想不到很好的办法,也可以直接上RMQ,​

现在,用​ 的解法~~~

维护一个单调自增的队列,同样,如果新加入的数字破坏了队列的单调性,说明队尾的数字将永远不会是这k个数字中的最小值,他已经没用了。

算法具体做法,可能我写的比较渣,大佬很好像合起来写的。

首先,将前k个数字入队列,队首元素就是最小值。

然后加入新的数字,如果破坏了单调性,弹出队尾数字,此时,又一个最小值求出来了,但是这个最小值还要检验一下,是否他还在下一个区间里面。


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <stack>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 1e6+;
int a[maxn];

int main()
{
//freopen("in.txt","r",stdin);
int n,k; cin>>n>>k;

for(int i = ; i < n; i++) scanf("%d",&a[i]);
deque<int> minq;
deque<int> maxq;

for(int i = ; i < k; i++) {
while(!minq.empty()&&(a[i]<a[minq.back()]) ) {
minq.pop_back();
}
while(!maxq.empty()&&(a[i]>a[maxq.back()])) {
maxq.pop_back();
}
minq.push_back(i);
maxq.push_back(i);
}

printf("%d ",a[minq.front()]);

if(minq.front()==)
minq.pop_front();

for(int i = k; i < n; i++) {
while(!minq.empty()&&(a[i]<a[minq.back()])) {
minq.pop_back();
}
minq.push_back(i);
printf("%d ",a[minq.front()]);
if(minq.front()<=i-k+)
minq.pop_front();
}
puts("");


printf("%d ",a[maxq.front()]);

if(maxq.front()==)
maxq.pop_front();

for(int i = k ; i < n; i++) {
while(!maxq.empty()&&(a[i]>a[maxq.back()])) {
maxq.pop_back();
}
maxq.push_back(i);
printf("%d ",a[maxq.front()]);
if(maxq.front()<=i-k+)
maxq.pop_front();
}
puts("");

return ;
}

例题四:bzoj 1911

分析:很容易想到类似于LIS的转移。但是数据范围有​ ,​ 是肯定过不了的。然而,只要稍加转换,

可以发现他是一个斜率公式,然后分析,斜率是凸函数,还是凹函数,只要看符号,这里是大于号,上凸函数中间的点的斜率是不起作用的,那么你应该维护一个斜率单调递增的栈(实际操作中是队列)。

那么最优值在哪里呢?

根据斜率最大,即应该是相切处,那么这时候,你需要根据凹函数的特点了,从前往后遍历,当斜率大于右边,则到达了当前点了。然后加入此节点,也需要维护单调队列的凹函数性质。

关于斜率DP,也是每一个大佬有一种写法,主要不同点在于队列中的点个数上,我习惯于队列中必须有一个结点(有的要两个点),主要是有一个坐标原点。可以实现包含所有可选择区间。具体细节还得自己动手才能发现。


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <string>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 1e6+;

long long a,b,c;
long long x[maxn];
long long sum[maxn];
long long d[maxn];
double slope(int i,int j)
{
double up = d[i]-d[j]+a*(sum[i]*sum[i]-sum[j]*sum[j])+b*(sum[j]-sum[i]);
double down = *a*(sum[i]-sum[j]);
return up/down;
}
int l,r,q[maxn];
int n;


int main()
{
freopen("in.txt","r",stdin);
scanf("%d%lld%lld%lld",&n,&a,&b,&c);

sum[] = ;
for(int i = ; i <= n; i++) {
scanf("%lld",&x[i]);
sum[i] = sum[i-] + x[i];
}

deque<int> deq;

for(int i = ; i <= n; i++) {

double xl = slope(q[l],q[l+]);
while(l<r&&slope(q[l],q[l+])<sum[i]) {
l++;
}
int now = q[l];
d[i]=d[now]+a*(sum[i]-sum[now])*(sum[i]-sum[now])+b*(sum[i]-sum[now])+c;
while(l<r&&slope(q[r-],q[r])>slope(q[r],i)) r--;
q[++r] = i;

}

printf("%lld\n",d[n]);


return ;
}

到这里DP优化已经聊的差不多了。其实你会发现我都是1D/1D方程,而实战中也有很多2D/0D方程,比如LCS。

但是他们的优化思路,是还有待大牛研究的课题。

ACM-ICPC (10/14)的更多相关文章

  1. 2016 ACM/ICPC Asia Regional Qingdao Online 1001/HDU5878 打表二分

    I Count Two Three Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  2. 2016 ACM/ICPC Asia Regional Qingdao Online(2016ACM青岛网络赛部分题解)

    2016 ACM/ICPC Asia Regional Qingdao Online(部分题解) 5878---I Count Two Three http://acm.hdu.edu.cn/show ...

  3. ACM ICPC Kharagpur Regional 2017

    ACM ICPC Kharagpur Regional 2017 A - Science Fair 题目描述:给定一个有\(n\)个点,\(m\)条无向边的图,其中某两个点记为\(S, T\),另外标 ...

  4. ACM/ICPC 之 BFS(离线)+康拓展开(TSH OJ-玩具(Toy))

    祝大家新年快乐,相信在新的一年里一定有我们自己的梦! 这是一个简化的魔板问题,只需输出步骤即可. 玩具(Toy) 描述 ZC神最擅长逻辑推理,一日,他给大家讲述起自己儿时的数字玩具. 该玩具酷似魔方, ...

  5. ACM ICPC 2015 Moscow Subregional Russia, Moscow, Dolgoprudny, October, 18, 2015 D. Delay Time

    Problem D. Delay Time Input file: standard input Output file: standard output Time limit: 1 second M ...

  6. hduoj 4715 Difference Between Primes 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4715 Difference Between Primes Time Limit: 2000/1000 MS (J ...

  7. hduoj 4712 Hamming Distance 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4712 Hamming Distance Time Limit: 6000/3000 MS (Java/Other ...

  8. hduoj 4707 Pet 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4707 Pet Time Limit: 4000/2000 MS (Java/Others)    Memory ...

  9. hduoj 4706 Children&#39;s Day 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4706 Children's Day Time Limit: 2000/1000 MS (Java/Others) ...

  10. 2016 ACM/ICPC Asia Regional Shenyang Online 1009/HDU 5900 区间dp

    QSC and Master Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

随机推荐

  1. 带OUTPUT的增删改

    sql server2005以后引入: 执行的sql语句中加入output可以事实输出处理的内容 go --插入并返回每行的插入值 DECLARE @NewRows TABLE(Id INT ,NAM ...

  2. mysql DCl语句

    DCl 语句主要书DBA用来管理系统中的对象权限使用 grant select,insert on sakila.* 'kingle'@'localhost' identified by '123'; ...

  3. (转)Nginx 反向代理、负载均衡、页面缓存、URL重写及读写分离详解

    原文:http://blog.51cto.com/freeloda/1288553 大纲 一.前言 二.环境准备 三.安装与配置Nginx 四.Nginx之反向代理 五.Nginx之负载均衡 六.Ng ...

  4. (转)用shell脚本实现杨辉三角的4个实例!

    概述:    中国古代数学家在数学的许多重要领域中处于遥遥领先的地位.中国古代数学史曾经有自己光辉灿烂的篇章,而杨辉三角的发现就是十分精彩的一页.杨辉三角形,是二项式系数在三角形中的一种几何排列.杨辉 ...

  5. oem的使用

    1 浏览器输入下面的网址: 虚拟机[安装orcale的机器]:http://localhost:1158/em/ 本机:http://192.168.47.10:1158/em/ 192.168.47 ...

  6. Android 软件自动更新功能实现的方法

    相信所有的用户都遇到过软件提醒更新的情况,下面就将实现此功能 首先看一下程序目录结构 步骤: 1.新建一个类UpdateManger,用于显示提示更新 详细出处参考:http://www.jb51.n ...

  7. JAVA 利用反射自定义数据层框架

    之前的随笔一直都在介绍c#,主要公司最近的业务都是做桌面程序,那么目前c#中的WPF肯定是我做桌面程序的不二之选,做了半年的WPF,也基本摸清了c#写代码的套路和规则(本人之前是两年多的JAVA开发者 ...

  8. 深入理解jQuery插件开发【转】

    如果你看到这篇文章,我确信你毫无疑问会认为jQuery是一个使用简便的库.jQuery可能使用起来很简单,但是它仍然有一些奇怪的地方,对它基本功能和概念不熟悉的人可能会难以掌握.但是不用担心,我下面已 ...

  9. C++ 编译器

    C++编译器 当我们定义了一个类的时候, C++编译器在默认的情况下会为我们添加默认的构造方法, 拷贝构造方法, 析构函数和=运算符 在第一次创建对象的语句中如: MyString myString ...

  10. python 对列表去重,并保持列表原来顺序

    mailto = ['cc', 'bbbb', 'afa', 'sss', 'bbbb', 'cc', 'shafa'] addr_to = list(set(mailto)) addr_to.sor ...