dp_单片机 - CSDN
dp 订阅
DisplayPort(简称DP)是一个由PC及芯片制造商联盟开发,视频电子标准协会(VESA)标准化的数字式视频接口标准。该接口免认证、免授权金,主要用于视频源与显示器等设备的连接,并也支持携带音频、USB和其他形式的数据。此接口的设计是为取代传统的VGA、DVI和FPD-Link(LVDS)接口。通过主动或被动适配器,该接口可与传统接口(如HDMI和DVI)向后兼容。 展开全文
DisplayPort(简称DP)是一个由PC及芯片制造商联盟开发,视频电子标准协会(VESA)标准化的数字式视频接口标准。该接口免认证、免授权金,主要用于视频源与显示器等设备的连接,并也支持携带音频、USB和其他形式的数据。此接口的设计是为取代传统的VGA、DVI和FPD-Link(LVDS)接口。通过主动或被动适配器,该接口可与传统接口(如HDMI和DVI)向后兼容。
信息
定    义
数字式视频接口标准
简    称
DP
开发者
PC及芯片制造商联盟
中文名
显示接口
学    科
通讯工程
外文名
DisplayPort
DisplayPort接口概观
DisplayPort是第一个依赖数据包化数据传输技术的显示通信端口,这种数据包化传输技术可以在以太网、USB和PCI Express等技术中找到。它既可以用于内部显示连接,也可以用于外部的显示连接。与过去的标准需要在每个输出端的差分对里面固定传输定时器信号不同的是,DisplayPort协议是基于小的数据报文被称为微报文,这种微报文可以将定时器信号嵌入在数据流中。其优点是较少的引脚数,就可以实现更高的分辨率。数据报文的应用也允许使用DisplayPort可扩展,这就意味着随着时间的推移,物理通信端口本身不需要显著的变化就可以增加额外的功能了。 [1]  DisplayPort可用于同时传输音频和视频,这两项中每一项都可以在没有另外一项的基础上单独传输。视频信号路径中每个颜色通道可以有6到16位,音频路径可以有多达8通道24位192 kHz的非压缩的PCM音频,或可以在音频流中封装压缩的音频格式。一个双向的、半双工的辅助通道携带了主链接用的设备管理和设备控制数据,如VESAEDID、MCCS和DPMS标准。此外,该通信端口是能够运送双向USB信号。DisplayPort信号不兼容DVI或HDMI。然而,双模式DisplayPorts被设计用来通过该通信端口传输单链接DVI或HDMI1.2/1.4的协议,需要通过使用一个外部无源连接器来实现,选择所需的信号,并将电气信号从LVDS转换为TMDS。带有被动连接器的双模DisplayPorts不支持VGA和双链接DVI。这些通信端口需要有源连接器来转换所需要输出的协议和信号。VGA连接器可以使用DisplayPort连接器来供电,而双链接DVI连接器可能依赖于外部电源(参考HDMI、DVI和VGA兼容性)。DisplayPort连接器在主链路可以有1、2、或4路差分数据对(巷道),每巷道可以在自定时器运行于162、270、或540MHz的基础上其原始码率为1.62、2.7或者5.4 Gbit/s。数据为8b/10b编码,即每8位的消息被编入10比特符号中。因此,解码后每通道的有效数据传输速率是1.296、2.16、4.32 Gbit/s(或者说是总量的80%)。
收起全文
精华内容
参与话题
  • DP专题

    千次阅读 2018-11-12 23:08:49
    DP,即动态规划。从高中学习编程以来,我就没有完整,认真地学习过它。可事实证明,出来混迟早是要还的。过去欠下的漏洞,从现在开始填补。 言归正传,在我看来,DP与递推,贪心,数学归纳法都有相似之处。它与递推...

    DP,即动态规划。从高中学习编程以来,我就没有完整,认真地学习过它。可事实证明,出来混迟早是要还的。过去欠下的漏洞,从现在开始填补。

    言归正传,在我看来,DP与递推贪心数学归纳法都有相似之处。它与递推一样,只关注上一步与下一步的关系,事实上,许多dp最后的代码实现就是通过递推来写。它又和贪心很像,都是只关注局部最优解。它又和数学归纳法极其类似:找到边界,确定两步之间的关系。根据此关系,即可推出任意一步的结果。(一己之见,欢迎dalao批评指正)

    以下给出例题(https://vjudge.net/contest/269597),为压缩篇幅,不贴完整代码,只给状态转移方程。

    A - To The Max

    大意:给一个N*N的数字矩形,求和最大子矩形

    此题为最大子段和的升级版,对于最大子段和,我们知道其状态转移方程为:

    dp[i]=max \left \{ dp[i-1],0 \right \}+a[i]

    事实上,我们只需先把上下两行确定,求出范围内每一列的和(即把矩阵压缩为一维数组),就可以把问题转化为求最大子段和。

    每一列之和可以在输入时即预处理好。

    B - Maximum sum

    大意:求最大的两段子段和

    分两种情况。对于以i结尾的这个状态,若只有一个子段,公式与最大子段和相同;若有两个子段,则先找到之前所有的单个子段中最大的那一个,比较它与以(i-1)结尾的,两个子段的大小,取较大的那一个,再加上a[i]自己

    dp[i][0]=max\left \{ dp[i-1][0],0 \right \}+a[i]

    dp[i][1]=max\left \{ dp[i-1][1],max\left ( dp[1\rightarrow i][0] \right ) \right \}+a[i]

    C - 最少拦截系统(导弹拦截)

    大意:此题原题有两问,第一问:求最长不上升子序列;第二问:求拦截所有导弹,最少需要多少套系统。此题只截取了第二问。

    对于第一问,很简单

    dp[i]=max\left \{ dp[j] | j<i \cap a[j]>a[i] \right \}+1

    第二问怎么求呢?有些人用dp+二分。但事实上,第二问就是在求最长上升子序列,证明如下

    1)假设打导弹的方法是这样的:取任意一个导弹,从这个导弹开始将能打的导弹全部打完。而这些导弹全部记为为同一组,再在没打下来的导弹中任选一个重复上述步骤,直到打完所有导弹。

    (2)假设我们得到了最小划分的K组导弹,从第a(1<=a<=K)组导弹中任取一个导弹,必定可以从a+1组中找到一个导弹的高度比这个导弹高(因为假如找不到,那么它就是比a+1组中任意一个导更高,在打第a组时应该会把a+1组所有导弹一起打下而不是另归为第a+1组),同样从a+1组到a+2组也是如此。那么就可以从前往后在每一组导弹中找一个更高的连起来,连成一条上升子序列,其长度即为K;

    (3)设最长上升子序列长度为P,则有K<=P;又因为最长上升子序列中任意两个不在同一组内(否则不满足单调不升),则有

    P>=K,所以K=P。

    所以......把第一问的符号改一下就好啦

    D - Brackets sequence

    大意:括号匹配加强版。给了一个括号序列(只有"("  ")"  "["  "]") 现在让添加括号,使括号序列变得匹配,要求添加最少的括号,且输出这个匹配的括号序列。

    区间DP。dp[i][j]表示区间i~j匹配添加括号后区间最小长度

    dp[i][j]=dp[i][k]+dp[k+1][j]

    注意当s[i]=='('&&s[j]==')' || s[i]=='['&&s[j]==']' 时,特判一下dp[i][j]=min(dp[i][j],dp[i+1][j-1]+2),这样可以找出匹配后的序列最小长度,但是题目要求输出匹配的序列,那么可以在定义一个数组v[i][j] 标记i~j区间的断开位置,如果s[i]=='('&&s[j]==')' || s[i]=='['&&s[j]==']' 时 v[i][j]==-1, 然后在递归调用输出即可;

    关于区间DP还不是很懂,等我再找一些题,下次开一篇新的博客专门写一下。

    展开全文
  • 动态规划(DP)

    2020-06-08 23:11:36
    DP

    动态规划(DP)
    用来优化加速,时间复杂度从指数(Exponential) 加速到多项式时间(Polynomial)。
    何时使用DP?

    • 计数问题:求有多少种方法
    • 优化问题:max和mim问题

    使用DP的要求

    • 最优子结构:
    • 重复子问题:求解子问题时,会出现重复计算(overlapping)。如果没有重复计算,则退化成分治算法(divide and conquer)
    • 无后效性:调用子问题最优解时,子问题的最优解不发生改变。

    使用方法

    1. 带有记忆性的递归
      举例:斐波那契序列
    2. DP

    典型题目

    斐波那契序列

    分析
    递推公式和边界条件已知。存在重复计算的问题,类似带有记忆性的递归,即使用数组保存先前的结果,减少重复计算。

    开一个数组dp[]
    for i in range(3,n)
    dp[i]=dp[i-1]+dp[i-2]
    仅需一个for loop

    代码

    int DP(int n)
    {
    	int dp[n+1];
    	dp[1]=1;dp[2]=1;
    	for(int i=3;i<=n;i++)
    		dp[i]=dp[i-1]+dp[i-2];
    	return dp[n];
    }
    

    三角形数

    题目
    给定一个由行数字组成的数字三角形。试着设计一个算法,计算出从三角形的顶到底的一条路径,使得该路径经过的数字总和最大。
    测试数据

    1
    2 3
    20 5 6
    7 8 9 10
    最大值 31

    思路
    自底向上方向,MaxSum[ i ] [ j ]表示第i行第j列的数到最底下路径和的最大值。
    MaxSum[ i] [ j] 和MaxSum[ i ][ j+1]中的较大者,作为第i-1行的转移项。
    代码

    #include<iostream>
    
    using namespace std;
    
    const int maxn=1e2+2;
    
    int D[maxn][maxn],MaxSum[maxn][maxn]; 
    int main()
    {
    	int i,j;
    	int N;
    	cin>>N;//输入N行测试数据
    	for(i=1;i<=N;i++)
    		for(j=1;j<=i;j++) 
    			cin>>D[i][j];
    
    	for(i=1;i<=N;i++)//最大权值和初始化为最后一列 
    		MaxSum[N][i]=D[N][i];
    		
    	for(i=N;i>1;i--)
    		for(j=1;j<i;j++)
    		{
    			if(MaxSum[i][j]>MaxSum[i][j+1])//比较左右两边数据大小 
    				MaxSum[i-1][j]=D[i-1][j]+MaxSum[i][j];//更新上一行最大权值的和 
    			else
    				MaxSum[i-1][j]=D[i-1][j]+MaxSum[i][j+1];
    		}
    	//最大值在MaxSum[1][1]
    	cout<<MaxSum[1][1]<<endl;	
    } 
    

    需要注意
    注意数组的边界,数组不是从0开始,这里使用从1开始。
    对于第i行,需要使用第i+1行的数据,对于for(i=N;i>1;i–)这里

    最小路径和

    类似的题目:Leetcode 64
    题目
    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
    说明:每次只能向下或者向右移动一步。

    示例:

    输入:
    [
    [1,3,1],
    [1,5,1],
    [4,2,1]
    ]
    输出: 7
    解释: 因为路径 1→3→1→1→1 的总和最小。

    代码

    class Solution {
    public:
        int minPathSum(vector<vector<int>>& grid) {
            int m=grid.size();
            int n=grid[0].size();
            vector<vector<int>> f(m,vector<int>(n,0));//m*n的两维vector,全0
            //状态
             f[0][0]=grid[0][0];
            for(int i=1;i<n;i++)//第一行
                f[0][i]+=grid[0][i]+f[0][i-1];
                
            f[0][0]=grid[0][0];
            for(int i=1;i<m;i++)//第一列
                f[i][0]+=grid[i][0]+f[i-1][0];
            //转移
            for(int i=1;i<m;i++)
                for(int j=1;j<n;j++)
                     f[i][j]=grid[i][j]+min(f[i-1][j],f[i][j-1]);            
            return f[m-1][n-1];
    
        }
    };
    

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/minimum-path-sum
    在这里插入图片描述

    最长上升子序列(LIS)

    问题描述
    一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8)。你的任务,就是对于给定的序列,求出最长上升子序列的长度。
    输入数据
    输入的第一行是序列的长度N(1<=N<=1000)。第二行给出的序列中的N个整数,这些整数的取值范围都是0-10000。
    输出要求
    最长上升子序列的长度。

    输入样例
    7
    1 7 3 5 9 4 8
    输出样例
    4

    分析
    求 a1_1,a2_2,…,ak_k,…,an_n 的最长上升子序列
    子问题是求 a1_1,a2_2,…,ak_k的最长上升子序列

    dp[i]表示终点是ai_i 的最长上升子序列的长度
    动态规划

    1. 状态转移 : dp[ i ] = max { dp[ j ]+1 , dp[ i ] } , 对于所有的 j< i ,并且 fj_j<fi_i
      含义: 以aj_j为终点的最长子序列长度加上 ai_i本身 就是 以 ai_i结尾的最长子序列的长度, 即 dp[ j ]+1
    2. base cases :dp[ i ] =1 ,i ∈ [0,n)
      含义:初始时每个位置的最长上升子序列长度都为1,即它本身

    时间复杂度 O(n2^2)

    代码

    #include<iostream>
    using namespace std;
    
    const int maxn=1010;
    int f[maxn],dp[maxn];
    //dp[i]表示终点是i的最长上升子序列的长度 
    int ans=1;//保存结果 
    int main()
    {
    	int N;
    	cin>>N;
    	for(int i=1;i<=N;i++)
    	{
    		cin>>f[i];
    		dp[i]=1;//边界处理 
    	}
    
    	//转移 
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=1;j<i;j++)
    		{
    			if(f[j]<f[i]) dp[i]=max(dp[i],dp[j]+1);
    		}		
    		ans=max(ans,dp[i]);
    	}
    	cout<<ans;		
    } 
    

    补充最长下降子序列(LDS)

    #include<iostream>
    
    using namespace std;
    const int maxn=1010;
    
    long long a[maxn],dp[maxn];
    
    //最长下降子序列 
    long long LDS( long long a[],int N)
    {
    	long long  temp=0;
    	for(int i=1;i<=N;i++)
    	 dp[i]=1;
    	 for(int i=1;i<=N;i++)
    	 {
    	 	for(int j=1;j<i;j++)
    	 	{
    	 		if(a[j]>a[i])
    	 			dp[i]=max(dp[j]+1,dp[i]);
    	 	}
    	 	temp=max(dp[i],temp);
    	 }
    	 
    	 return temp;
    }
    //最长上升子序列 
    long long LIS( long long a[],int N)
    {
    	long long  temp=0;
    	for(int i=1;i<=N;i++)
    	 dp[i]=1;
    	 for(int i=1;i<=N;i++)
    	 {
    	 	for(int j=1;j<i;j++)
    	 	{
    	 		if(a[j]<a[i])
    	 			dp[i]=max(dp[j]+1,dp[i]);
    	 	}
    	 	temp=max(dp[i],temp);
    	 }
    	 
    	 return temp;
    }
    
    int main()
    {
    	
    	int N;
    	cin>>N;
    	for(int i=1;i<=N;i++) 
    		{
    			cin>>a[i];
    		}
    	cout<<LDS(a,N)<<endl;
    	cout<<LIS(a,N)<<endl; 
    }
    

    最长公共子序列

    Leetcode1143
    问题描述
    输入两个字符串, 要你求出两个字符串的最长公共子序列长度。
    输入
    输入两行不超过200的字符串。
    输出
    给出两个字符串的最大公共字符串的长度。

    样例输入
    abcfbc
    abfcab
    样例输出
    4

    分析
    状态转移
    我们需要看字符串 s1 第i个字母 和 字符串 s2 第j个字母的关系
    if s1i_i == s2j_j
    同时去掉各自最后的字母,看s1i_i-1_1 和 s2j_j-1_1
    得到 dp[ i ] [ j ] = dp [ i-1 ] [ j -1 ]+1

    if s1i_i != s2j_j
    若去掉 s1i_i ,看 s1i_i-1_1 和 s2j_j 的公共子序列 与
    若去掉 s2j_j,看s1i_i和 s2j_j-1_1的公共子序列
    取两者最大值 dp[ i ] [ j ] = max (dp [ i ] [ j-1 ],dp [ i-1] [ j ] );

    代码

    class Solution {
    public:
        int longestCommonSubsequence(string text1, string text2) {
            int m=text1.size();
            int n=text2.size();
            int dp[m+1][n+1];
            
           memset(dp,0,sizeof(dp));//置零
            for(int i=1;i<=m;i++)
                for(int j=1;j<=n;j++)
                {
                    if(text1[i-1]!=text2[j-1])
                    dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
    
                    if(text1[i-1]==text2[j-1])
                    dp[i][j]=dp[i-1][j-1]+1;
                }
            return dp[m][n]; 
        }
    };
    

    将字符串翻转成单增

    Leetcode 926
    如果一个由 ‘0’ 和 ‘1’ 组成的字符串,是以一些 ‘0’(可能没有 ‘0’)后面跟着一些 ‘1’(也可能没有 ‘1’)的形式组成的,那么该字符串是单调递增的。

    我们给出一个由字符 ‘0’ 和 ‘1’ 组成的字符串 S,我们可以将任何 ‘0’ 翻转为 ‘1’ 或者将 ‘1’ 翻转为 ‘0’。

    返回使 S 单调递增的最小翻转次数。

    示例 1:
    输入:“00110”
    输出:1
    解释:我们翻转最后一位得到 00111.
    示例 2:
    输入:“010110”
    输出:2
    解释:我们翻转得到 011111,或者是 000111。
    示例 3:
    输入:“00011000”
    输出:2
    解释:我们翻转得到 00000000。
    提示:
    1 <= S.length <= 20000
    S 中只包含字符 ‘0’ 和 ‘1’

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/flip-string-to-monotone-increasing

    分析
    求最长不降子序列(LIS),字符串长度减去LIS 即可
    使用 DP算法求解LIS超时。

    超时代码(思路是对的)

    class Solution {
    public:
        int minFlipsMonoIncr(string S) {
            int len=S.size();
            int dp[len+1];
            for(int i=0;i<=len;i++)
                dp[i]=1;
             int ans=0;
            for(int i=0;i<len;i++)
            {
                for(int j=0;j<i;j++)
                {
                    if(S[j]<=S[i])
                    dp[i]=max(dp[i],dp[j]+1);
                }
                ans=max(dp[i],ans);
            }
            return len-ans;
        }
         
    };
    

    优化代码(AC)
    维护单调数列,使用二分法。

    采用upper_bound()函数,返回第一个大于x的位置这样的话,。遇到相等的元素,则一直会添加到数组中来
    时间复杂度O(nlogn)
    空间复杂度O(n)

    class Solution {
    public:
        int minFlipsMonoIncr(string S) {
            int len=S.size();
            vector<char> vec;
            for(int i=0;i<len;i++)
            {
                //p是大于S[i]的下标
                int p = upper_bound(vec.begin(),vec.end(),S[i])-vec.begin();
                if(vec.size()==p) //新来的是最大的
                    vec.push_back(S[i]);//加入数组
                else//新来的比原位置的小
                    vec[p]=S[i];替换掉
            }
           return len-vec.size();//返回字符串长度-LIS
        }
         
    };
    
    
    作者:shizhengli
    链接:https://leetcode-cn.com/problems/flip-string-to-monotone-increasing/solution/zui-chang-bu-jiang-shang-sheng-zi-xu-lie-by-shizhe/
    来源:力扣(LeetCode)
    

    补充一道简单的dp题

    链接:迷雾森林
    来源:牛客网

    赛时提示:保证出发点和终点都是空地

    帕秋莉掌握了一种木属性魔法
    这种魔法可以生成一片森林(类似于迷阵),但一次实验时,帕秋莉不小心将自己困入了森林
    帕秋莉处于地图的左下角,出口在地图右上角,她只能够向上或者向右行走

    现在给你森林的地图,保证可以到达出口,请问有多少种不同的方案

    答案对2333取模

    输入描述:
    第一行两个整数m , n表示森林是m行n列
    接下来m行,每行n个数,描述了地图
    0 - 空地
    1 - 树(无法通过)
    输出描述:
    一个整数表示答案
    此题需要在计算过程中%2333,同时在处理边界的时候,出现1的话后面的都不能通过;这里处理是多用了一行的一个格,省掉了一行和一列的边界处理。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3010;
    
    int a[maxn][maxn];
    int n,m;
    long long  dp[maxn][maxn];
    
    
    template<class T>inline void read(T &res)
    {
    	char c;T flag=1;
    	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    
    
    int main(){
    	cin>>m>>n;
    	for(int i=1;i<=m;i++){
    		for(int j=1;j<=n;j++)
    			read(a[i][j]);
    	}
    	memset(dp,0,sizeof(dp));
    	dp[m+1][1]=1;
    	
    	for(int i=m;i>=1;i--){
    		for(int j=1;j<=n;j++){
    			if(a[i][j]!=1)
    			dp[i][j]=(dp[i+1][j]+dp[i][j-1])%2333;
    		}
    	}
    	
    		cout<<dp[1][n]<<endl;
    //	cout<<endl;
    //	for(int i=1;i<=m;i++){
    //		for(int j=1;j<=n;j++)
    //			cout<<dp[i][j]<<" ";
    //		cout<<endl; 
    //	}
    	
    	
    
    	
    	
    }
    
    /*
    3 5
    0 1 0 0 0
    0 0 0 0 0
    0 0 1 0 0
    
    
    */
    
    展开全文
  • 动态规划(DP)算法

    万次阅读 多人点赞 2018-04-23 00:10:15
    动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法。利用各个阶段之间的关系,逐个求解,最终求得全局最优解,需要确认原问题与子问题、动态规划状态、边界状态、边界状态结值、状态转移方程。...

    动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法。利用各个阶段之间的关系,逐个求解,最终求得全局最优解,需要确认原问题与子问题、动态规划状态、边界状态、边界状态结值、状态转移方程。

    一、爬楼梯leetcode70:

    You are climbing a stair case. It takes n steps to reach to the top.

    Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

    Note: Given n will be a positive integer.

    Example 1:

    Input: 2
    Output: 2
    Explanation: There are two ways to climb to the top.
    1. 1 step + 1 step
    2. 2 steps
    

    Example 2:

    Input: 3
    Output: 3
    Explanation: There are three ways to climb to the top.
    1. 1 step + 1 step + 1 step
    2. 1 step + 2 steps
    3. 2 steps + 1 step

    方法一:利用n个楼梯的步数,与n-1还有n-2之间的关系可以退出,f(n)==f(n-1)+f(n-2),相当于是直接考虑为n-1再上一步,和n-2直接上两步,不能考虑n-2有两种走法(一步一步,和一次两步,一步一步的会和n-1中的重复,导致算多了),最后不断的迭代直至可以n==1或者n==2,可以直接求出结果。

    这个方法相当于是根据各个阶段之间的关系,列出迭代关系,并且写出临界解,从而结束递归的过程,否则将一直递归下去(所有的递归都是如此,如果没有边界条件提前结束递归,递归将不会停止)

    这个时间复杂度是2^n相当于是一颗二叉树来着,leetcode显示time limit exceed

    int climbStairs(int n) {
            if(n==1||n==2){
                return n;
            }
            return climbStairs(n-1)+climbStairs(n-2);    
       }

    方法二:利用迭代来实现尾递归

    由于方法一是利用了尾递归来实现算法,考虑采用迭代来实现递归,并且递归本身算法复杂度是要远远大于其对应的迭代循环算法复杂度的,所以考虑利用迭代来减少时间复杂度。两种方法的差别在于递归是从上往下算,迭代是从下往上算。

    class Solution {
    public:
        int climbStairs(int n) {
            vector<int>iteration(n+1,0); //initializition
            iteration[1]=1;
            iteration[2]=2;
            int i=3;
            while(i<n+1){
                iteration[i]=iteration[i-1]+iteration[i-2];
                i++;
            }
            return iteration[n];  
        }
    };

    时间复杂度是O(n),相比较于尾递归大大优化,leetcode显示ac。

    二、抢劫犯问题leetcode198

    You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

    Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

    题目分析:这个题目的分析关键在于DP算法中状态转移方程的求解,也就是求解是迭代关系,可以发现对于第i个房间而言,如果不抢劫第i个房间那么就是i-1个的抢劫数目,如果抢劫第i个房间那么就是一定不能抢劫i-1个房间,相当于是i-2个房间的抢劫数目,两者时间的最大值即可,从而成功得出迭代关系。

    注意自身在分析问题时的错误:

    一:在无法的出迭代关系的情况下,没有考虑根据题意堆可能的情况进行分类,注意两个题目都是进行了分类的讨论,才得以顺利的得出迭代关系,并且盲目的理解为迭代关系一定是两者之间的和,没有考虑到最大值得情况。

    二:在考虑迭代关系时一定要思考如何引入第i-1个和第i-2个问题的解,这道题就是通过分类讨论,成功剥离出了i-1和i-2的情况;迭代关系的另一个要素是如何把i与i-1和i-2之间的关系找到

    三:在考虑迭代关系时一定把i考虑成足够大,因为在代码实现过程中i很小的情况是直接给出的,直接赋值的(对于有dp数组而言),i很小的情况下只是考虑为边界条件,作为循环的起始或者是迭代的结束。所以考虑迭代关系时一定不要具体化i而是直接假设i足够大去考虑。

    求取迭代关系的步骤:

    1、根据题意分类讨论,分类讨论一定要达到引入i-1和i-2的解

    2、挖掘i和i-1还有i-2之间的关系

    3、边界条件确认

    方法一、使用迭代法

    class Solution {
    public:
        int rob(vector<int>& nums) {
            if(nums.empty()) return 0;
            if(nums.size()==1) return nums[0];
            vector<int>dp(nums.size(),0);
            dp[0]=nums[0];
            dp[1]=max(nums[0],nums[1]);
           for(int i=2;i<nums.size();i++){
                dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
            }
            return dp[nums.size()-1];
        }
    };

    时间复杂度为O(n),运行时间3ms

    方法二、使用递归算法

    class Solution {
    public:
        int rob(vector<int>& nums) {
            int size=nums.size();
            if(size==0) return 0;
            if(size==1) return nums[0];
            if(size==2) return max(nums[0],nums[1]);
            vector<int>a1(nums.begin(),nums.end()-1);
            vector<int>a2(nums.begin(),nums.end()-2);
            return max(rob(a1),rob(a2)+nums[size-1]);
         
        }
    };

    可以发现这种方法再次出现了time limit exceed,时间复杂度是O(2^n),以后不用再考虑递归的DP算法了,直接使用迭代,时间复杂度降低很多。

                        三、最大子段和leetcode53

    Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

    注意自身在分析问题时的错误:

           在分析问题的时候没有灵活变通,直接考虑前i个连续子数组数组的最大值,将无法进行分类讨论,无法得到递归关系,并且在考虑递归关系时也是直接考虑了i与i-1还有i-2之间的关系,其实可以考虑为i与i-1的关系即可,只要是一种可以迭代出所有情况的关系即可。在不能够得出迭代关系的时候需要变通的考虑,改变dp数组的意义,不需要一步到位,只要保证可以通过dp数组得到最后的结果即可。

    code:dp数组表示的是以第i个元素结尾的连续子数组的最大值,最后再寻找dp的最大值

    class Solution {
    public:
        int maxSubArray(vector<int>& nums) {
            int size=nums.size();
            vector<int>dp(size,0);
            dp[0]=nums[0];
            for(int i=1;i<size;i++){
                if(dp[i-1]>0) dp[i]=dp[i-1]+nums[i];
                else dp[i]=nums[i];
            }
            int max1=dp[0];
            for(int i=1;i<size;i++){
                max1=max(max1,dp[i]);
            }
            return max1;
        }
    };

    从第四题开始之后的题目都会较为复杂的情况

    四、找零钱和leetcode322(非常经典的DP算法)

    You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

    Example 1:
    coins = [1, 2, 5], amount = 11
    return 3 (11 = 5 + 5 + 1)

    Example 2:
    coins = [2], amount = 3
    return -1.

    Note:
    You may assume that you have an infinite number of each kind of coin.

    注:这道题自己还是没有能够理解到迭代关系的思想,可以再看一下solution,此时实在无法理解

    注意自身在分析问题时的错误:(假设已经理解了迭代关系)

    一、这个迭代关系比较特殊,并不是i-1,i-2之类的,而是会变化的,随着coins的不同会发生改变,所以需要对coins进行遍历,在循环中加入条件判断顺便很好地解决了是否会越界的问题。

    ....没有理解这道题果然写不出总结

    class Solution {
    public:
        int coinChange(vector<int>& coins, int amount) {
            vector<int>dp(amount+1,amount+1);
            dp[0]=0;
            for(int i=1;i<=amount;i++){
                for(int m:coins){
                    if(m<=i) dp[i]=min(dp[i],dp[i-m]+1);
                }            
            }
            if(dp.back()==amount+1) return -1;
                else return dp.back();
        }
    };

    五、三角形

    Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

    For example, given the following triangle

    [
         [2],
        [3,4],
       [6,5,7],
      [4,1,8,3]
    ]
    

    The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

    这道题目类似于第三题,都是对dp数组的意义进行转换,从而以退为进解决问题,这道题目是自己独立完成的,所以就不写心得了,直接上代码
    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& triangle) {
            int size=triangle.size();
            vector<vector<int>>dp(size,vector<int>(size,INT_MAX));
            dp[0][0]=triangle[0][0];
            for(int i=1;i<size;i++){
                for(int j=0;j<triangle[i].size();j++){
                    if(j==0) dp[i][j]=dp[i-1][j]+triangle[i][j];
                    if(j==triangle[i].size()-1) dp[i][j]=dp[i-1][j-1]+triangle[i][j];
                    if(j!=0&&j!=triangle[i].size()-1) dp[i][j]=min(dp[i-1][j-1]+triangle[i][j],dp[i-1][j]+triangle[i][j]);              
                }
            }
            return *min_element(dp[size-1].begin(),dp[size-1].end());
        }
    };
    















    展开全文
  • 期末了,通过写博客的方式复习一下dp,把自己理解的dp思想通过样例全部说出来说说我所理解的dp思想dp一般用于解决多阶段决策问题,即每个阶段都要做一个决策,全部的决策是一个决策序列,要你求一个最好的决策...
        

    期末了,通过写博客的方式复习一下dp,把自己理解的dp思想通过样例全部说出来

    说说我所理解的dp思想

    dp一般用于解决多阶段决策问题,即每个阶段都要做一个决策,全部的决策是一个决策序列,要你求一个

    最好的决策序列使得这个问题有最优解

    将待求解的问题分为若干个相互联系的子问题,只在第一次遇到的时候求解,然后将这个子问题的答案保存

    下来,下次又遇到的时候直接拿过来用即可


    dp和分治的不同之处在于分治分解而成的子问题必须没有联系(有联系的话就包含大量重复的子问题,那

    么这个问题就不适宜分治,虽然分治也能解决,但是时间复杂度太大,不划算),所以用dp的问题和用分

    治的问题的根本区别在于分解成的子问题之间有没有联系,这些子问题有没有重叠,即有没有重复子问题


    dp和贪心的不同之处在于每一次的贪心都是做出不可撤回的决策(即每次局部最优),而在dp中还有考察

    每个最优决策子序列中是否包含最优决策子序列,即是否具有最优子结构性质,贪心中每一步都只顾眼前

    最优,并且当前的选择是不会依赖以前的选择的,而dp,在选择的时候是从以前求出的若干个与本步骤

    相关的子问题中选最优的那个,加上这一步的值来构成这一步那个子问题的最优解


    讲得再多不如看几个很经典的样例,带你初步入门dp

    我不会讲很多具体该怎么做,而是剖析这些经典例题中的dp思想,真真正正的1懂得了dp思想的话,做题事

    半功倍(自己深有体会)


    样例1:数字三角形问题

    7

    3 8

    8 1 0

    2 7 4 4

    4 5 2 6 5

    从顶部向下走,每次只能走下面或者右下,走完全程,问你怎么走使得权值最大(问题描述不是很详细,关

    于数字三角形问题是什么问题请百度)

    那么dp的思想到底是怎么体现的呢?

    dp是要先分解成很多相互联系的子问题,要解决一个子问题,依赖于前面和此子问题相关的已经解决的子

    问题中选一个最优的加上这个子问题的解,就是这个子问题的最优解

    具体做法:

    1.分析问题的最优解,找出最优解的性质,并刻画其结构特征:

    问题的最优解:所有走法中最大的权值是多少?

    最优解的性质和结构特征:只能向正下或者右下走,每走一行的最大权值等于前面一行的最大权值加上这一

    行的走的两个方向中的最大值

    2.递归的定义最优值:

    要找到从0行出发的最优值,就要找到从第1行出发的最优值

    要找到从1行出发的最优值,就要找到从第2行出发的最优值

    ………………………

    要找到第3行出发的最优值,就要找到从最后一行出发的最优值

    为什么是这样呢?我们分析一下

    题目要你求从0行出发的最优值,那么我们就是要找到从第一行出发的最优值,加上第0行到第1行的最优值

    但是,很重要的一点,我们需要递归求解,要先求解从倒数第一行出发的最优值,然后根据从倒数第一行出

    发的最优值求出从倒数第二行出发的最优值

    3.采用自底向上的方式计算问题的最优值:

    这个就是我上面说的,要先求解从倒数第一行出发的最优值,然后根据从倒数第一行出发的最优值求出从倒

    数第二行出发的最优值,自底向上的计算,迭代的方式求解子问题

    4.根据计算最优值时间得到的信息,构造最优解

    这个就是问你具体是怎么走的,我们需要在求解子问题的时候保存一些信息,采用构造出最优解(最优值和

    最优解是不同的,最优值在本问题中是一个走法中权值之和最大的那一个,而最优解是具体的走法),这里

    题目没有要求就是不用去构造最优解,构造起来也挺麻烦的。。。。

    解法:

    dp【i】【j】:代表从第i行第j列出发得到的最优值

    dp【i】【j】=max(dp【i+1】【j】,dp【i+1】【j+1】)+a【i】【j】

    表示从第i行第j列出发的最优值等于到i+1行的两种走法中最大的那一个加上出发点的权值

    贴个链接:

    https://www.cnblogs.com/yinbiao/p/8995253.html

    贴个代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n;
        scanf("%d",&n);//n行
        int a[n][n];
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<=i;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        int dp[n][n];
        memset(dp,0,sizeof(dp));
        for(int j=0;j<n;j++)
        {
            dp[n-1][j]=a[n-1][j];
        }
        for(int i=n-2;i>=0;i--)
        {
            for(int j=0;j<=i;j++)
            {
                dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
            }
        }
        printf("%d\n",dp[0][0]);
        return 0;
    }


    经典样例2:最长公共子序列问题 (LCS问题)

    给你两个序列,问你他们的最长LCS序列的长度是多少?(序列可以是不连续的,只要元素的相对位置一

    样)(不了解LCS问题的自行百度)

    那么在LCS问题中dp的思想体现在哪里呢?

    重复子问题:(超级容易发现的一个)

    我们要求x1~xi,Y1~Yj的LCS,那么是不是要求x1~xi-1,Y1~Yi-1的LCS

    我们要求x1~xi-1,y1~yi-1的LCS,那么是不是要求x1~xi-2,Y1~yi-2的LCS

    所以我们要求的x1~xi,Y1~Yj的LCS这个大问题中,包含了很多的重复子问题

    具体做法:

    c【i】【j】表示x1~xi,Y1~Yj的LCS序列长度

    x【i】==y【j】 c【i】【j】=c【i-1】【j-1】+1

    x【i】!=y【j】  c【i】【j】=max(c【i-1】【j】,c【i】【j-1)

    i==0||j==0 c【i】【j】=0

    贴个代码(求最优值和最优解)

    #include<bits/stdc++.h>
    #define max_v 1005
    using namespace std;
    char x[max_v],y[max_v];
    int dp[max_v][max_v];
    int l1,l2;
    int dfs(int i,int j)
    {
        if(i==-1||j==-1)
            return 0 ;
        if(x[i]==y[j])//来自左上角
        {
            dfs(i-1,j-1);
            cout<<x[i]<<" ";//先递归到最后再输出,,这样就是顺序的
        }
        else
        {
            if(dp[i-1][j]>dp[i][j-1])//来自上面
            {
                dfs(i-1,j);
            }
            else//来自左边
            {
                dfs(i,j-1);
            }
        }
        return 0;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        getchar();
        while(t--)
        {
            scanf("%s",x);
            scanf("%s",y);
            int l1=strlen(x);
            int l2=strlen(y);
            memset(dp,0,sizeof(dp));
            for(int i=1; i<=l1; i++)
            {
                for(int j=1; j<=l2; j++)
                {
                    if(x[i-1]==y[j-1])
                    {
                        dp[i][j]=dp[i-1][j-1]+1;
                    }
                    else
                    {
                        dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                    }
                }
            }
            printf("%d\n",dp[l1][l2]);
            dfs(l1,l2);
            cout<<endl;
        }
        return 0;
    }
    
    
    /*
    2
    ABCBDAB
    BDCABA
    */


    经典样例三:矩阵连乘问题,纸牌问题,石头合并问题(都是一类问题,一起分析)

    给定n个矩阵{A1,A2…..An},其中A【i】与A【i+1】是可乘的,如何确定计算的次序,使得乘法的总次数最少

    首先我们要明白,计算的次序不同,那么乘法的总次数也不同

    类似的问题:给你n张牌,每张排都有一个数字,相邻的两张牌的权值可以相乘,相乘的两张牌可以合并为

    一张牌,新牌的权值是原来的两张牌的乘积

    这个问题还有石头合并问题都是同一类的问题,属于区间dp问题

    石头合并问题:给你一堆石头,排成一行,相邻的两个石头可以合并,合并成的石头的权值为原来两个石头

    的权值之和

    先来分析矩阵连乘问题:

    给你一个一维数组

    30,35,15,5,10,20,25

    只要相邻的矩阵才可以相乘

    思考一下,dp的思想是如何体现的

    第一步我们是要把问题分解成很多互相有联系的子问题(重复子问题是用dp的基础)

    简单的思考一下,每次矩阵相乘,最简单的就是两个可以相乘的矩阵相乘(A1,A2,A3),那最大的乘法次数就是A1*A2*A3

    但是如果是多个呢,我们是不是可以简化成下面这样

    A【i】,A【i+1】………………….A【k】………………A【j-1】,A【j】

    讲他们分成两个抽象矩阵

    第一个:A【i】….A【k】

    第二个:A【k+1】…..A【j】

    把大问题抽象成两个抽象矩阵相乘,那么更加最简单的那种抽象一下就知道求所有矩阵乘法的最大次数,就

    是求第一个抽象矩阵自己内部要乘的次数和第二个抽象矩阵内部自己要求的乘法次数然后加上这这两个抽象

    矩阵合并为一个大的抽象矩阵要乘的次数

    那么大问题是这样的,大问题里面是不是有很多这样的小问题,而且这些小问题还是重复的,比如A【k】

    的选择不同,那么乘的次序结果也不一样,A【k】的选择可以导致很多问题都有重复的部分,如果多次计

    算的话,无疑是很不明智的,这样的话跟分治就是没有什么区别了,这样的问题就叫做重复子问题

    A【k】的选择不同的话,会导致子问题有很多重复的部分,前面我们说了的,同时A【k】的选择不同的话

    会导致两个抽象矩阵相乘的结果也不一样,所以我们就要在所有的A【k】选择中找一个最小的

    所以我们现在在这个问题里面找到了dp思想的具体体现:大量的重复子问题

    具体做法:

    dp【i】【j】:代表矩阵i,矩阵i+1………….矩阵j的最少乘法次数

    总结上述:

    dp【i】【j】=min(dp【i】【k】+dp【k+1】【j】

    i<=k<=j-1

    贴个代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define max_v 1005
    int dp[max_v][max_v],a[max_v],s[max_v][max_v];
    void f(int i,int j)
    {
        if(i==j)
            return ;
        f(i,s[i][j]);
        f(s[i][j]+1,j);
        printf("A[%d:%d]*A[%d:%d]\n",i,s[i][j],s[i][j]+1,j);
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
    
        for(int i=1;i<=n;i++)
        {
            dp[i][i]=0;
        }
    
        for(int r=2;r<=n;r++)
        {
            for(int i=1;i<=n-r+1;i++)
            {
                int j=i+r-1;
                dp[i][j]=dp[i+1][j]+a[i-1]*a[i]*a[j];
                s[i][j]=i;
                for(int k=i+1;k<j;k++)
                {
                    int t=dp[i][k]+dp[k+1][j]+a[i-1]*a[k]*a[j];
                    if(t<dp[i][j])
                    {
                        dp[i][j]=t;
                        s[i][j]=k;
                    }
                }
            }
        }
        f(1,n);
    }
    
    /*
    6
    30 35 15 5 10 20 25
    
    A[2:2]*A[3:3]
    A[1:1]*A[2:3]
    A[4:4]*A[5:5]
    A[4:5]*A[6:6]
    A[1:3]*A[4:6]
    */

    分析了矩阵连乘问题,再来分析一下石头合并问题

    石头合并问题:其实这个问题跟矩阵连乘问题真的是一样的

    非常非常的类似

    A1,A2………………….An

    也是分解成两个抽象的石头

    A【i】,A【i+1】………A【k】……….A【j】

    第一个抽象石头:A【i】……..A【k】

    第二个抽象石头:A【k+1】…….A【j】

    我们现在把大问题分解成了两个抽象的石头合并问题

    问的是你合并完成后最小的权值是多少

    大问题的最小权值等于第一个抽象石头合并的权值加上第二个抽象石头合并的权值,再加上这两个抽象的石头合并的权值

    我们知道,A【k】的选择不同,会导致最后权值的不同,也会导致大量重复的子问题(前面在矩阵连乘wen他中具体分析了)

    所以我们要在所有的A【k】选择中,选择一个合并花费最小的

    现在我们把大问题分解成了这样一个问题,那么每个抽象的石头也还可以当初一个大问题继续分解呀,所以

    就分解成了很多子问题

    具体做法:

    dp【i】【j】:代表合并第i到第j个石头的最小花费

    sum【i】:表示1~i个石头的权值之和

    dp【i】【j】=min(dp【i】【k】+dp【k+1】【j】)+sum【j】-sum【i】+a【i】

    为什么是sum【j】-sum【i】+a【i】呢?

    因为我们要合并从第i个石头到第j个石头所需要的花费就是第i个石头到第j个石头的权值的和呀

    贴个代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            int a[n+1];
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&a[i]);
            }
            int sum[n+1];
            int dp[n+1][n+1];
            for(int i=1; i<=n; i++)
            {
                int t=0;
                for(int j=1; j<=i; j++)
                {
                    t=t+a[j];
                }
                sum[i]=t;
            }
            for(int i=1; i<=n; i++)
            {
                dp[i][i]=0;
            }
            for(int r=2; r<=n; r++)
            {
                for(int i=1; i<=n-r+1; i++)
                {
                    int j=i+r-1;
                    int t=dp[i][i]+dp[i+1][j]+sum[j]-sum[i]+a[i];
                    for(int k=i; k<=j-1; k++)
                    {
                        if(t>dp[i][k]+dp[k+1][j]+sum[j]-sum[i]+a[i])
                        {
                            t=dp[i][k]+dp[k+1][j]+sum[j]-sum[i]+a[i];
                        }
                    }
                    dp[i][j]=t;
                }
            }
            printf("%d\n",dp[1][n]);
        }
        return 0;
    }
    
    /*
    样例输入
    3
    1 2 3
    7
    13 7 8 16 21 4 18
    样例输出
    9
    239
    */


    经典样例四:最长递增子序列

    比如

    1,7,3,5,8,4,8

    问你最长的递增的子序列的长度是多少

    这个问题的最优解有多个,但是最优值只有一个:长度为4

    1,3,5,9

    1,3,5,8

    1,3,4,8

    这三个都是最优解,但是他们长度都是一样的,长度为4

    这些是我们看出来的

    那我们如何用dp的思想解题呢

    第一步分解成很多互相有联系的子问题

    要求第n个元素结尾的LIS序列的长度,就要求以第n-1个元素结尾的LIS序列的长度

    要求第n-1个元素结尾的LIS序列的长度,就要求以第n-2个元素结尾的LIS序列的长度

    …………..

    假设第n-1个元素结尾的LIS序列的长度为2,且第n个元素是大于第n-1个元素的(递增的),那么以第n

    个元素结尾的LIS序列的长度不就是以第n-1个元素结尾的LIS序列的长度加上1吗?

    再回过头来看看这些子问题

    他们中是不是含有大量重复的子问题

    dp【n】:代表以第n个元素结尾的LIS序列的长度

    比如我要求dp【n】,就要求dp【n-2】,dp【n-3】

    在要求dp【n-1】的时候,也还要求dp【n-2】,dp【n-3】一次

    这个就是求了很多次,想想当n足够大的时候,子问题足够多的时候,求的重复的子问题是不是很多很多

    这样的话速度太慢

    所以这个时候,dp的作用就是体现出来了,保存已经求解过的子问题的值,下次又遇到这个子问题的时

    候,直接拿出来用就好啦

    做法:

    dp【1】=1

    dp【i】=max(dp【j】+1) 要求:a【j】<a【i】,j<i

    就是在第i个元素的前面找到LIS序列长度最大的,加上1,(先决条件是递增的)

    贴个代码:(最优值和一个最优解)

    #include<bits/stdc++.h>
    using namespace std;
    #define max_v 1005
    int a[max_v],dp[max_v];
    void f(int n,int result)
    {
        bool flag=false;
        if(n<0||result==0)
            return ;
        if(dp[n]==result)
        {
            flag=true;
            result--;
        }
        f(n-1,result);
        if(flag)
            printf("%d ",a[n]);
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            for(int i=0;i<n;i++)
            {
                scanf("%d",&a[i]);
            }
            memset(dp,0,sizeof(dp));
            dp[0]=1;
            for(int i=1;i<n;i++)
            {
                int t=0;
                for(int j=0;j<i;j++)
                {
                    if(a[j]<a[i])
                    {
                       if(t<dp[j])
                       {
                           t=dp[j];
                       }
                    }
                }
                dp[i]=t+1;
            }
            int t=0;
            for(int i=0;i<n;i++)
            {
                if(t<dp[i])
                {
                    t=dp[i];
                }
            }
            printf("%d\n",t);
            f(n,t);
            printf("\n");
        }
        return 0;
    }
    /*
    输入:
    7
    1 7 3 5 9 4 8
    输出:
    4
    1 3 4 8*/


    经典样例五:最大子段和问题

    比如:

    -2,11,-4,13,-5,-2

    什么叫最大字段和?就是连续的数字的和最大是多少,注意是段,而不是序列,序列可以是离散的,而段必

    须的连续的

    所以这个问题dp思想体现在哪里呢?

    这个问题其实跟LIS,LCS问题都差不多,都是线性dp问题

    第一步:分解成很多有联系的子问题

    要求以第n个元素结尾的最大字段和是多少,就要求以第n-1个元素结尾的最大字段和是多少

    要求以第n-1个元素结尾的最大子段和是多少,就要求以第n-2个元素结尾的最大字段和是多少

    为什么是这样呢?

    仔细思考一下

    以求第n-1个元素的1最大字段和为例

    如果我们知道了以第n-2个元素的最大字段和是多少,如果是正的,加上第n个元素值即可,如果是负数,

    那还不如不加呢,这样第n个元素的最大字段和还大一点,因为你加上一个负数肯定比原来的数小了呀

    那么dp思想中的重复子问题体现在哪里呢?

    体现在第一步,跟LIS问题中的体现是一样的,这里不再赘述

    贴个代码:(最优解)

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            int n;
            scanf("%d",&n);
            int a[n+1];
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
            }
            int dp[n+1];
            dp[1]=a[1];
            for(int i=2;i<=n;i++)
            {
                int x=dp[i-1];
                if(x<0)
                {
                    x=0;
                }
                dp[i]=x+a[i];
            }
            int maxvalue=dp[1];
            for(int i=2;i<=n;i++)
            {
                if(maxvalue<dp[i])
                {
                    maxvalue=dp[i];
                }
            }
            printf("%d\n",maxvalue);
        }
        return 0;
    }
    /*
    输入:
    2
    6
    -2 11 -4 13 -5 -2
    输出;
    20
    */


    经典样例六:01背包问题

    背包问题可以说是很经典的问题之一了,01背包问题,就是说每个物品只有两种选择,装还不装,且物品

    不可分割

    我先不讲01背包问题应该怎么做,讲01背包里面蕴含的dp思想

    dp适用于多阶段决策问题,就是每个阶段都要做决策,且你做的决策会影响的最终的结果,导致最终结果的值有所不同

    这个决策的概念在01背包里面用的可以说是体现的非常非常的透彻了,因为你每个阶段都要做决策呀,这

    个物品我到底是选还是不选呢

    声明一个 大小为  m[n][c] 的二维数组,m[ i ][ j ] 表示 在面对第 i 件物品,且背包容量为  j 时所能获得的最大价值 ,那么我们可以很容易分析得出 m[i][j] 的计算方法,

    (1). j < w[i] 的情况,这时候背包容量不足以放下第 i 件物品,只能选择不拿

    m[ i ][ j ] = m[ i-1 ][ j ]

    (2). j>=w[i] 的情况,这时背包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大的价值。

    如果拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价值,也是相当于为第i件物品腾出了w[i]的空间。

    如果不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)

    究竟是拿还是不拿,自然是比较这两种情况那种价值最大。

    状态转移方程:

    if(j>=w[i])
         m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
    else
         m[i][j]=m[i-1][j];

    代码如下:(二维dp解决01背包问题,一维dp解决01背包问题)

    #include<bits/stdc++.h>
    using namespace std;
    int ZeroOnePack(int v[],int w[],int n,int c)//v1,v2....vn价值  w1,w2,w3...wn重量 n表示n个物品 c表示背包容量
    {
        int dp[n+1][c+1];
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<=c; j++)
            {
                if(j>=w[i])
                {
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//第i个物品放入之后,那么前面i-1个物品可能会因为剩余空间不够无法放入
                }
                else
                {
                    dp[i][j]=dp[i-1][j];
                }
    
            }
        }
        return dp[n][c];
    }
    //空间优化,采用一维数组
    int ZeroOnePack_improve(int v[],int w[],int n,int c)//v1,v2....vn价值  w1,w2,w3...wn重量 n表示n个物品 c表示背包容量
    {
        int dp[c+1];
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=n; i++)
        {
            for(int j=c; j>=0; j--)
            {
                if(j>=w[i])
                    dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    
            }
        }
        return dp[c];
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
    
            int n,c;
            scanf("%d %d",&n,&c);
            int v[n+1],w[n+1];
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&v[i]);
            }
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&w[i]);
            }
            // printf("%d\n",ZeroOnePack(v,w,n,c));
            printf("%d\n",ZeroOnePack_improve(v,w,n,c));
    
        }
        return 0;
    }
    
    /*
    1
    5 10
    1 2 3 4 5
    5 4 3 2 1
    
    14
    */
    展开全文
  • 理论与实战相结合,通过一些例题,帮助同学们掌握贪心与DP算法的相关知识。
  • dp详解

    2019-08-01 20:25:04
    Lmemset放在外面idonnot konw why
  • 动态规划浅析(DP

    2020-09-13 00:04:58
    动态规划步骤 动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维...类似于归纳法的,当我们要计算 dp[n] 时,是可以利用 dp[n-1],dp[n-2].....dp[1],
  • 区间dp入门

    万次阅读 多人点赞 2020-04-07 21:06:21
    什么是区间dp?顾名思义:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。二.核心思路既然让我求解在一个区间上的最优解,那么我把这...
  • 状态压缩dp(状压dp)

    千次阅读 多人点赞 2019-02-28 21:28:12
    状压dp是一类比较难理解的dp; 在讲状压dp之前,我们应该清楚所有的dp是解决多阶段决策最优化问题的一种思想方法; 请注意多阶段这三个字: 经过前面三种背包的学习,可以发现如何定义状态是解决动态规划最重要的...
  • DP

    2019-03-14 22:29:57
    T1 P1855 榨取kkksc03 洛谷的运营组决定,如果一名oier向他的教练推荐洛谷,并能够成功的使用(成功使用的定义是:该团队有20个或以上的成员,上传10道以上的私有题目,布置过一次作业并成功举办过一次公开比赛)...
  • 算法-动态规划 Dynamic Programming--从菜鸟到老鸟

    万次阅读 多人点赞 2017-07-18 09:17:08
    前言最近在牛客网上做了几套公司的真题,发现有关动态规划(Dynamic Programming)算法的题目很多。相对于我来说,算法里面遇到的问题里面感觉最难的也就是动态规划(Dynamic Programming)算法了,于是花了好长时间...
  • DP 合集

    2019-08-13 21:09:37
    写这篇博客是为了将这几天写的DP(递推)题的思路总结一下,免得以后忘了,毕竟现在还学得糊里糊涂。。。 A 数塔 题意:给你一个数塔只能从根节点忘左右儿子走,问怎么走到底层使得经过的数之和最大? 思路: ...
  • Android中dp和px之间进行转换

    万次阅读 多人点赞 2011-12-02 09:35:35
    在xml布局文件中,我们既可以设置px,也可以设置dp(或者dip)。一般情况下,我们都会选择使用dp,这样可以保证不同屏幕分辨率的机器上布局一致。但是在代码中,如何处理呢?很多控件的方法中都只提供了设置px的方法...
  • 数位dp总结 之 从入门到模板

    万次阅读 多人点赞 2017-12-11 11:02:02
    数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp咯。数位还算是比较好听的名字,数位的含义:一个数有个位、十位、百位、千位......数的每...
  • dp2px px2dp

    万次阅读 2017-01-19 15:45:29
    /** * 将dp转换成px * @param context * @param dpValue * @return */ public static int dip2px(Context context,float dpValue){ final float scale = context.getResources ().g
  • 区间DP小结(附经典例题)

    万次阅读 多人点赞 2019-06-18 08:36:36
    ——这篇文章主要想总结下区间DP的经典题目,同时给自己复习巩固这方面知识点。 区间DP 一、定义 区间DP,顾名思义是在区间上DP,它的主要思想就是先在小区间进行DP得到最优解,然后再利用小区间的最优解...
  • 再次调试STM32F407+DP83848

    万次阅读 多人点赞 2017-11-29 17:12:20
    早在还没有毕业前,就调试过STM32F407+DP83848,这次又调试了一次,居然花了2天时间。STM32支持两种工业级标准的接口,来与外部物理层 PHY模块相连,分别是独立于介质的接口(MII)和简化的独立于接口的接口(RMII...
  • 颜值与质量俱佳的4K miniDPDP线

    千次阅读 2019-06-13 11:25:28
    现在常见的DisplayPort和苹果开发的miniDP是号称目前业内最顶级的DP(DisplayPort)接口,DP接口起始标准发轫于2006年,由视频电子标准协会(VESA)全权制定。DisplayPort拥有全面超越HDMI的整体素质,重点在于它是...
  • DP-java版本

    千次阅读 2017-05-02 18:46:25
    常见dp问题的java叙述总结。背包问题0-1背包对于0-1背包问题,运用dp的思想可以有两种常见状态转移:认为物品下标从0开始 dp[i][j]表示从前i-1个物品选,在重量不超过j的情况下的最大价值。(正向) dp[i][j]表示从...
  • Android dp方式的屏幕适配-原理 传统所说的屏幕适配,其实是针对不同屏幕的UI尺寸适配,即在编写页面时使用了具体的dp、sp值后导致的其他问题。 我之前写过一些相关的文章和工具插件,也收到了很多网友的反馈。 ...
1 2 3 4 5 ... 20
收藏数 655,208
精华内容 262,083
关键字:

dp