动态规划——线性DP.1

动态规划算法通常用于求解具有某种最优性质的问题。

那它和贪心有区别吗?

当然有。不然叫动态规划干啥?

幼儿园英语老师:DP是啥?

小盆友:Dog&Peppa pig

英语老斯:恩恩!真聪明!

然而,你是小盆友吗?

如果是

如果不是,

DP是D****** P*******的缩写。

意思是动态规划。

聪明的bolt告诉你:是Dynamic Programming的缩写!!!

动态规划注重     表示状态,转移状态

so

讲一个栗子:

LIS:

最长上升子序列

这是线性动态规划中最经典的栗子之一。

最长上升子序列(Longest Increasing Subsequence,LIS),指一个序列中最长的单调递增的子序列。

注意不是子串,所以可以不相邻。

比如说:

序列:3   2   1   4   6   8   7   9

它的LIS是5

3   4   6   8   9

或3   4   6   7   9

或2   4   6   8   9

······

还有很多种情况。

于是我们珂以得出:

动态规划的最优解,有不同的组合情况,但答案只有一个。

所以,如果NOIP出了动态规划的题目时,一般会叫你求值,而不是求情况。

这是好处!

BUT,有的老师不会好心,会给更多限制条件,使Ans只有一种情况,那就更有难度了。

LIS问题要用动态规划做。

方法一:

这是一个好理解的方法。

但是更好使耗时

不难看出,dp [ i ]就是第 i 个数的LIS

那代码怎么实现的呢?

先别急,我们在举个生活中的栗子。

老师要你算1+2+3+4+5+6+7+8+9=?时,你会算得45,

老师再问你1+2+3+4+5+6+7+8+9+10=?时,你是会用1+···+10,还是用之前算的45+10?

聪明人会用后面一种。

所以,我们根据这个方便的原理,发现我每次计算dp [ i ] 时,如果用到了前面的 dp 值,则会减少一定的计算量。

在我们每次枚举一个数的dp值时,只要扫描在它前面比它小的数,那些比他小的数的dp值的最大值+1就是所求数的dp值

因为比所求数小的数的dp值表示它的LIS,再来一个比它大的数,大树数的LIS就等于小数的LIS+1.

但由于小数的LIS有大有小,我们又要求最长子序列,我们就要取最大值。

一番思考后,我们找到了状态转移方程,也就是动态规划中最重要的东西:

对于每一个 i ,我们枚举它前面的数 j,if (i > 它前面的数 j )   dp [ i ] = max ( dp [ i ] , dp [ j ] + 1 ) ;

这个算法的时间复杂度是O(n^2)的,慎用。

code:

 1 int n,a[1001]/*用来存序列*/,dp[1001]/*dp值*/;//数组大小根据题目而定。
 2 cin>>n;
 3 dp[1]=1;                                   //1的dp值为1
 4 for(int i=1;i<=n;i++)
 5     cin>>a[i];
 6 for(int i=1;i<=n;i++)
 7 {
 8     for(int j=1;j<i;j++)
 9     {
10         if(a[i]>a[j])
11         {
12             dp[i]=max(dp[i],dp[j]+1);      //状态转移
13         }
14     }
15 }
16 cout<<dp[n]<<endl;

注意要初始化dp [ 1 ] = 1.剩下的为 0.

还有另一种时间复杂度为 n log n 的LIS算法

看,栗子!

2   1   5   3   6   4   6   3

在 dp 值相同的情况下,保留较小的数显然更好。因为后面的数若能跟较大的数构成上升子序列,也一定能能较小的数构成上升子序列,反之则不一定。例如 a_3=5 与 a_4=3 的 dp 均为 2,但 a_6=4 不能与 a_3=5 构成上升子序列,而可以和 a_4=3 构成上升子序列。 因此,不同的 dp 值只需要存一个对应的最小值,将这个最小值顺序排列,他们一定是升序(严格来说是不下降)的。 于是,借助二分查找的方式,就可以很快查到更新的值,整体时间复杂度 O(nlogn)。

这个就是上面的一个优化,也没有太多可讲的,自己打一遍代码也就熟了。

code:

 1 const int maxn=1e5+5;
 2 int a[maxn];
 3 int n;
 4 int dp[maxn];
 5 int ans=1;
 6 int find(int x){
 7     int l=1,r=ans,m;
 8     while(l<r){
 9         m=l+(r-l)/2;
10         if(dp[m]>=a[x]){
11             r=m;
12         }
13         else{
14             l=m+1;
15         }
16     }
17     return l;
18 }//二分查找
19 int main(){
20     scanf("%d",&n);
21     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
22     dp[1]=a[1];
23     for(int i=2;i<=n;i++){
24         if(a[i]>dp[ans]){
25             dp[++ans]=a[i];
26         }
27         else{
28             int pos=find(i);
29             dp[pos]=a[i];
30         }
31     }
32     printf("%d",ans);
33     return 0;
34 }

这就是LIS问题,希望大家好好理解这个问题,因为他真的狠重要!

今天的分享就到这里,我们下次见。

原文地址:https://www.cnblogs.com/sillyben/p/9915268.html

时间: 2024-07-31 21:15:36

动态规划——线性DP.1的相关文章

动态规划——线性dp

我们在解决一些线性区间上的最优化问题的时候,往往也能够利用到动态规划的思想,这种问题可以叫做线性dp.在这篇文章中,我们将讨论有关线性dp的一些问题. 在有关线性dp问题中,有着几个比较经典而基础的模型,例如最长上升子序列(LIS).最长公共子序列(LCS).最大子序列和等,那么首先我们从这几个经典的问题出发开始对线性dp的探索. 首先我们来看最长上升子序列问题. 这个问题基于这样一个背景,对于含有n个元素的集合S = {a1.a2.a3……an},对于S的一个子序列S‘ = {ai,aj,ak

POJ 1958 Strange Towers of Hanoi (四塔问题,线性dp,记忆化搜索)

题目分析:四柱汉诺塔.由于题目已经给出了求解方法,直接写代码即可.下面总结一下,四塔问题. 感谢这篇文章的作者,点这里就到,总结的很好.直接贴过来~ 四塔问题:设有A,B,C,D四个柱子(有时称塔),在A柱上有由小到大堆放的n个盘子. 今将A柱上的盘子移动到D柱上去.可以利用B,C柱作为工作栈用,移动的规则如下: ①每次只能移动一个盘子. ②在移动的过程中,小盘子只能放到大盘子的上面. 设计并实现一个求解四塔问题的动态规划算法,并分析时间和空间复杂性. 算法思想: 用如下算法移动盘子(记为Fou

CH5102 Mobile Service【线性dp】

5102 Mobile Service 0x50「动态规划」例题 描述 一个公司有三个移动服务员,最初分别在位置1,2,3处.如果某个位置(用一个整数表示)有一个请求,那么公司必须指派某名员工赶到那个地方去.某一时刻只有一个员工能移动,且不允许在同样的位置出现两个员工.从 p 到 q 移动一个员工,需要花费 c(p,q).这个函数不一定对称,但保证 c(p,p)=0.给出N个请求,请求发生的位置分别为 p_1~p_N.公司必须按顺序依次满足所有请求,目标是最小化公司花费,请你帮忙计算这个最小花费

uva 11584 Partitioning by Palindromes 线性dp

// uva 11584 Partitioning by Palindromes 线性dp // // 题目意思是将一个字符串划分成尽量少的回文串 // // f[i]表示前i个字符能化成最少的回文串的数目 // // f[i] = min(f[i],f[j-1] + 1(j到i是回文串)) // // 这道题还是挺简单的,继续练 #include <algorithm> #include <bitset> #include <cassert> #include <

uva 11552 Fewest Flops 线性dp

// uva 11552 Fewest Flops // // 二维线性dp // // 首先,在该块必须是相同的来信.首先记录每块有很多种书 // 称为是counts[i]; // // 订购f[i][j]它代表前i字母j为结尾的最小分块数 // // 假设第i块的開始字母与第i-1块的结束字母同样 // f[i][j] = min(f[i][j],f[i-1][k] + counts[i] - 1); // // 否则 // // f[i][j] = min(f[i][j],f[i-1][k

poj3267——线性dp

poj3267——线性dp The Cow Lexicon Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8458   Accepted: 3993 Description Few know that the cows have their own dictionary with W (1 ≤ W ≤ 600) words, each containing no more 25 of the characters 'a'

线性DP POJ2279 Mr.Young&#39;s Picture Permutations

Mr. Young's Picture Permutations Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1128   Accepted: 562 Description Mr. Young wishes to take a picture of his class. The students will stand in rows with each row no longer than the row behin

HDU 5074 Hatsune Miku (线性dp)

Hatsune Miku Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 654    Accepted Submission(s): 471 Problem Description Hatsune Miku is a popular virtual singer. It is very popular in both Japan

Codeforces 176B (线性DP+字符串)

题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28214 题目大意:源串有如下变形:每次将串切为两半,位置颠倒形成新串.问经过K次变形后,与目标串相同的变形方案数.mod 1000000007. 解题思路: 奇葩的字符串DP.照着别人的题解写的,解释不出原理是什么. 首先统计出经过1次变形,就能和目标串相同的中间产物串(包含源串)的个数cnt.len表示源串长度,那么len-cnt就表示和目标串不同的个数. 用