线性DP详解
顾名思义,线性DP就是在一条线上进行DP,这里举一些典型的例子。
LIS问题(最长上升子序列问题)
题目
给定一个长度为N的序列A,求最长的数值单调递增的子序列的长度。
上升子序列B可表示为B={Ak1,Ak2,···,Akp},其中k1<k2<···<kp。
解析
状态:F[i]表示以A[i]为结尾的最长上升子序列的长度,边界为f[0]=0。
状态转移方程:F[i]=max{F[j]+1}(0≤j<i,A[j]<A[i])。
答案显然为max{F[i]}(1≤i≤N)。
事实上,无论是上升、下降还是不上升等等此类问题,代码都是相似的,唯一的区别只是判断的符号更改罢了。
Code
#include <iostream>
using namespace std;
int n,a[],f[],maxn;
int main()
{
f[]=;
cin>>n;
for(int i=;i<=n;i++)
{
cin>>a[i];
for(int j=;j<n;j++)
if(a[j]<a[i]) f[i]=max(f[i],f[j]+);
maxn=max(maxn,f[i]);
}
cout<<maxn;
return ;
}
LCS问题(最长公共子序列)
题目
给定两个长度分别为N、M的字符串A和B,求最长的既是A的子序列又是B的子序列的字符串的长度。
解析
状态:F[i][j]表示A的前i个字符与B的前j个字符中的最长公共子序列,边界为F[i][0]=F[0][j]=0。
状态转移方程:F[i][j]=max{F[i-1][j],F[i][j-1],F[i-1][j-1]+1(if A[i]=B[j])}。
答案为F[N][M]。
Code
#include <iostream>
using namespace std;
int n,m,f[][];
char a[],b[];
int main()
{
cin>>n>>m;
for(int i=;i<=n;i++)
{
cin>>a[i];
f[i][]=;
}
for(int i=;i<=m;i++)
{
cin>>b[i];
f[][i]=;
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
f[i][j]=max(f[i-][j],f[i][j-]);
if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-][j-]+);
}
cout<<f[n][m];
return ;
}
数字三角形
题目
给定一个N行的三角矩阵A,其中第i行有i列,从左上角出发,每次可以向下方或向右下方走一步,最终到达底部。
求把经过的所有位置上的数加起来,和最大是多少。
解析
状态:F[i][j]表示走到第i行第j列,和最大是多少,边界为F[1][1]=A[1][1]。
状态转移方程:F[i][j]=A[i][j]+max{F[i-1][j],F[i-1][j-1](if j>1)}。
答案为max{F[N][j]}(1≤j≤N)。
Code
#include <iostream>
using namespace std;
int n,a[][],f[][],maxn;
int main()
{
cin>>n;
for(int i=;i<=n;i++)
for(int j=;j<=i;j++) cin>>a[i][j];
f[][]=a[][];
for(int i=;i<=n;i++)
for(int j=;j<=i;j++)
{
f[i][j]=a[i][j]+f[i-][j];
if(j>) f[i][j]=max(f[i][j],a[i][j]+f[i-][j-]);
}
for(int i=;i<=n;i++) maxn=max(maxn,f[n][i]);
cout<<maxn;
return ;
}
例题一:合唱队形
题目
【题目描述】
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1≤i≤K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
【输入格式】
共二行。
第一行是一个整数N(2≤N≤100),表示同学的总数。
第二行有n个整数,用空格分隔,第i个整数Ti(130≤Ti≤230)是第i位同学的身高(厘米)。
【输出格式】
一个整数,最少需要几位同学出列。
【输入样例】
【输出样例】
【数据规模】
对于50%的数据,保证有n≤20;
对于全部的数据,保证有n≤100。
解析
最少出列,就是最多留下。
分析一下队形,其实质便是先上升再下降,不难联想到最长上升子序列与最长下降子序列。
定义状态F[i][0/1],F[i][0]表示以第i个人为结尾的最长上升子序列,F[i][1]表示以第i个人为结尾的最长合唱队形,F数组初值都为1,。
所以前i个人的最长合唱队形为max(F[i][0],F[i][1])。
状态转移方程:
F[i][0]=max(F[i][0],F[j][0]+1(if T[i]>T[j]));(T[i]如题所述,j<i)
F[i][1]=max(F[i][1],a[j][0],a[j][1]+1(if T[i]<T[j]);
最后的答案为n-max(F[i][0],F[i][1])。
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
int n,t[],f[][],ans;
int main()
{
cin>>n;
for(int i=;i<=n;i++) cin>>t[i];
for(int i=;i<=n;i++)
{
f[i][]=;
for(int j=;j<i;j++)
if(t[i]>t[j]) f[i][]=max(f[i][],f[j][]+);
}
for(int i=;i<=n;i++)
{
f[i][]=;
for(int j=;j<i;j++)
if(t[i]<t[j]) f[i][]=max(f[i][],max(f[j][],f[j][])+);
}
for(int i=;i<=n;i++) ans=max(ans,max(f[i][],f[i][]));
cout<<n-ans;
return ;
}
例题二:导弹拦截
题目
【题目描述】
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【输入格式】
1行,若干个整数。
【输出格式】
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【输入样例】
【输出样例】
【数据规模】
导弹高度是≤50000的正整数,导弹个数≤100000。
注:O(n2) 100分,O(nlogn) 200分。
解析
非常经典的一道线性DP题目。
先来说说O(n2)的做法:
第一问显然是在求最长不上升子序列,定义状态F[i]表示以第i个数为结尾的最长不下降子序列。
状态转移方程:F[i]=max(F[i],F[j]+1(if a[i]<=a[j]))(a[i]表示第i个导弹的高度,j<i)。
第二问实际上是在求最长上升子序列,证明比较麻烦,这里便不给出了,自行理解一下。
与第一问求法相同,只需要把<=改成>即可。
这种做法只有100分,考虑一下优化。
以第一问为例,观察样例,不难发现,当F的值相同时,越后面的导弹高度越高。
所以我们可以用一个d[i]维护F值为i的序列的最后一个导弹的值,t记录当前求出的最长不上升子序列的长度,
然后在递推时判断a[d[i]](1≤i≤t)的值,若大于等于当前导弹高度,就更新F。
第二问同理。
优化之后,虽然依旧是O(n2)的做法,但其时间复杂度却很小,足以拿到200分。
O(nlogn)的做法有很多,例如二分、线段树什么的,这里便不再给出(懒),有兴趣的可以自己尝试做做。
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
int a[],f[],n,maxn=-;
int main()
{
while(scanf("%d",&a[++n])==) ;
n--;
for(int i=;i<=n;i++)
{
f[i]=;
for(int j=;j<i;j++)
if(a[i]<=a[j]) f[i]=max(f[i],f[j]+);
maxn=max(maxn,f[i]);
}
printf("%d\n",maxn);
maxn=-;
for(int i=;i<=n;i++)
{
f[i]=;
for(int j=;j<i;j++)
if(a[i]>a[j]) f[i]=max(f[i],f[j]+);
maxn=max(maxn,f[i]);
}
printf("%d",maxn);
return ;
}
O(n^2) 100分做法
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long f[],a[],tot=,s=,d[],t=;
int main()
{
memset(d,,sizeof(d));
while(scanf("%lld",&a[++tot])==);
tot--;
for(int i=;i<=tot;i++)
{
f[i]=;
for(int j=t;j>=;j--)
if(a[i]<=a[d[j]])
f[i]=max(f[i],f[d[j]]+);
t=max(f[i],t);
d[f[i]]=i;
}
cout<<t<<endl;
t=;
for(int i=;i<=tot;i++)
{
f[i]=;
for(int j=t;j>=;j--)
if(a[i]>a[d[j]])
f[i]=max(f[i],f[d[j]]+);
t=max(f[i],t);
d[f[i]]=i;
}
cout<<t;
return ;
}
O(n^2)优化 200分做法
注:参考书籍:《算法竞赛进阶指南》
线性DP详解的更多相关文章
- 数位DP 详解
序 天堂在左,战士向右 引言 数位DP在竞赛中的出现几率极低,但是如果不会数位DP,一旦考到就只能暴力骗分. 以下是数位DP详解,涉及到的例题有: [HDU2089]不要62 [HDU3652]B-n ...
- 动态规划晋级——HDU 3555 Bomb【数位DP详解】
转载请注明出处:http://blog.csdn.net/a1dark 分析:初学数位DP完全搞不懂.很多时候都是自己花大量时间去找规律.记得上次网络赛有道数位DP.硬是找规律给A了.那时候完全不知数 ...
- 树形DP详解+题目
关于树形dp 我觉得他和线性dp差不多 总结 最近写了好多树形dp+树形结构的题目,这些题目变化多样能与多种算法结合,但还是有好多规律可以找的. 先说总的规律吧! 一般来说树形dp在设状态转移方程时都 ...
- Canvas使用渐变之-线性渐变详解
在canvas里面,除了使用纯色,我们还能把填充和笔触样式设置为渐变色:线性渐变和径向渐变. 线性渐变 createLinearGradient(x0,y0,x1,y1) 返回 CanvasGrad ...
- 数位DP详解
算法使用范围 在一个区间里面求有多少个满足题目所给的约束条件的数,约束条件必须与数自身的属性有关 下面用kuangbin数位dp的题来介绍 例题 不要62 题意:在一个区间里面求出有多少个不含4和6 ...
- Android开发之线性布局详解(布局权重)
布局权重 线性布局支持给个别的子视图设定权重,通过android:layout_weight属性.就一个视图在屏幕上占多大的空间而言,这个属性给其设 定了一个重要的值.一个大的权重值,允许它扩大到填充 ...
- 状压DP详解(位运算)
前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...
- 状态压缩dp 状压dp 详解
说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科 https://baike ...
- 数位dp详解&&LG P2602 [ZJOI2010]数字计数
数位dp,适用于解决一类求x~y之间有多少个符合要求的数或者其他. 例题 题目描述 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除 ...
随机推荐
- git create remote branch (五)
admin@PC-panzidong MINGW64 ~/WebstormProjects/backEndServer (master) 查看本地分支信息$ git branch* master ad ...
- s3git 使用git 管理云存储
使用s3git 我们可以方便的基于git协议进行s3存储数据的版本管理,同时也提供了一个方便的golang 包, 我们可以集成到我们的应用中,但是有一点,目前已经没有再更新过了,但是设计理论很不错,实 ...
- 47、Spark SQL核心源码深度剖析(DataFrame lazy特性、Optimizer优化策略等)
一.源码分析 1. ###入口org.apache.spark.sql/SQLContext.scala sql()方法: /** * 使用Spark执行一条SQL查询语句,将结果作为DataFram ...
- CSS Variables:css自定义属性的使用
CSS Variables,一个并不是那么新的东西,但对css来说绝对是一场革命.之前使用变量的时候,需要借助sass.less等预处理工具来实现,现在我们可以直接使用css来声明变量. 一.兼容性 ...
- 在Matlab中的tick可以调整方向
需要将axis对话框的More property打开,修改TickDir,可从In改成Out.
- struct udphdr
udphdr结构包含在/usr/src/linux/include/linux/udp.h struct udphdr { __u16 source; __u16 dest; __u16 len; _ ...
- mysql regexp 表达式
mysql> select * from test; +----+----------+-------+-----------+ | id | name | score | subject | ...
- 利用python做矩阵的简单运算(行列式、特征值、特征向量等的求解)
import numpy as np lis = np.mat([[1,2,3],[3,4,5],[4,5,6]]) print(np.linalg.inv(lis)) # 求矩阵的逆矩阵 [[-1. ...
- mysql 存储过程 REPEAT ... UNTIL ... END REPEAT
begin declare b int; declare use_no varchar(10); declare use_name varchar(400); decla ...
- java String 转Json报错 java.lang.NoClassDefFoundError: org/apache/commons/lang/exception/NestableRuntim
这个问题就是缺少jar包依赖!!! java.lang.NoClassDefFoundError: org/apache/commons/beanutils/DynaBean 缺少commons-be ...