动态规划的四个姿势

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

言归正传了!!!

之所以写动态规划优化,是因为常常在动态规划时,你会发现,就算你很轻易地定义出状态,和找到状态转移方程,但是你有可能面临时间限制。动态规划的优化,主要体现在一维上,一维已经很成熟了,也有很多专家研究这个,关于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. JS如何获取屏幕、浏览器及网页高度宽度?

    屏幕的尺寸是指当前分辨率下的高度.宽度,而不是物理高度.宽度. 如:一个22寸的显示器,屏幕分辨率为1366 * 768,那么我们可以获取到的屏幕高度为1366px,宽度为768px. 屏幕宽度和高度 ...

  2. Oracle broker--详解

    1,简介 01,介绍 Data Guard broker是建立在Data Guard基础上的一个对Data Guard配置,集中管理操作的一个平台.我们再上次DG主备切换的时候会发现特别麻烦,为此br ...

  3. apply、call、bind区别、用法

    apply和call都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数内部this的指向):   如果使用apply或call方法,那么this指向他们的第一个参数,apply的第二个参数是 ...

  4. js、css的阻塞问题

    js.css的阻塞问题 这篇文章主要是探索js.css的加载顺序及其影响问题. 下面的代码可以让浏览器阻塞: <!DOCTYPE html> <html lang="en& ...

  5. 阿冰教你一步一步做Android新闻客户端(二)两种异步线程加载图片的方法

    哈哈哈抱着没人看的心态随便写,直接上代码,各位看官看注释 一种Thread  一种AsyncTask 先不说用框架 public class ImageLoader { private ImageVi ...

  6. TOJ 1258 Very Simple Counting

    Description Let f(n) be the number of factors of integer n. Your task is to count the number of i(1 ...

  7. 《springcloud 四》服务保护机制

    服务保护机制SpringCloud Hystrix 微服务高可用技术 大型复杂的分布式系统中,高可用相关的技术架构非常重要. 高可用架构非常重要的一个环节,就是如何将分布式系统中的各个服务打造成高可用 ...

  8. NETCDF入门

    转载自:http://www.cnblogs.com/davidgu/p/3572317.html 一.概述  NetCDF全称为network Common Data Format,中文译法为“网络 ...

  9. [转]什么?你还不会写JQuery 插件

    本文转自:http://www.cnblogs.com/joey0210/p/3408349.html 前言 如今做web开发,jquery 几乎是必不可少的,就连vs神器在2010版本开始将Jque ...

  10. 线程操作之Thread类

    在.Net fremework 中,所有与线程有关的操作都封装在System.Thread命名空间中, 所以在操作线程时,要先引入次命名空间 Thread类的常用方法 Abort 线程终止 Join ...