刷题之路:DP思想(动态规划)
dp一般用于解决决策问题,比如说你的每一步都有好几种处理方式,怎么选择使得最后的结果满足或者接近于你的预期是需要考虑的问题。
所以dp问题实际上也就是最优解的问题
一般采用的方式就是将问题拆分成若干个子问题(有点像分治?),然后对每个子问题进行最优解寻找,保存结果,重复上述过程(有点像贪心?)。
根据网上大佬们所说,dp与分治的不同之处在于dp处理的是有可能相互影响的子问题,而分治处理的是毫无干系的子问题。而dp与贪心不同之处在于dp每次的结果并不是直接输出而是保存下来看有没有更优秀的解法,而贪心做出的都是不可撤回的决策(即每次选取最优解,达成局部最优)。可以认为dp判断是否是最优解根据的是它的子序列,而贪心则是每一步取最优解。
那么知道了这些,我们对dp思想已经有了一个基本的了解。
要解决dp问题,实际上也就是通过定义状态和状态转移两个步骤进行求解。
那么接下来我们就进行一些练习
接下来也就是介绍我学习到的第一个dp问题
也就是数字三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
像这样一种三角形,从顶部向下走,每次只能走下面或者右下,走完全程,问你怎么走使得权值最大。
这就是很经典的dp问题
那么自然是两步,定义状态与状态转移
定义状态:mymax[i][j]表示从(1,1)出发走到mymax[i][j]所有路径中的最大值
那么mymax[3][2]也就是它的左上和正上比较,即mymax[3][2]=max(7+3+1,7+8+1)=16
然后,确定了这一个状态(把它当成已知),那么我们就会想要知道,我们通过这个状态可以得出什么?
首先,我们需要考虑哪些状态会对mymax【i】【j】这个状态产生影响
那么自然是
【i-1】【j】->【i】【j】
【i-1】【j-1】->【i】【j】
这两种情况会对其产生影响,那么可以编写程序
mymax[i][j]=max(mymax[i-1][j],mymax[i-1][j-1])+a[i][j]
通过这两个影响量我们可以转移到mymax【i】【j】去。
那么造表,对最后一行进行遍历,就解决啦~
对了,记得初始化的时候把mymax初始化为负数哈~
当然,造表求解必然是一种解法,但还有一种思考的角度就是
如果我已知第i行的最大值,那我便可以转移到第i-1行的最大值,即自底向上去寻找最优解。
mymax[i][j]是第i行第j列到最底下路径和的最大值,那么就可以将mymax[i][j]和mymax[i][j+1]中的较大者,作为第i-1行的转移项。
for(i=N;i>1;i--)for(j=1;j<i;j++){if(mymax[i][j]>mymax[i][j+1])//比较两边数据大小 mymax[i-1][j]=a[i-1][j]+mymax[i][j];//更新上一行最大权值的和 elsemymax[i-1][j]=a[i-1][j]+mymax[i][j+1];//更新上一行最大权值的和 }
那么mymax[0][0]就是我们要的结果。
还有也可以自上而下递推
#include <iostream>
#include <math.h>
using namespace std;
int a[105][105];
int b[105][105];
int n;int main()
{cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=i;j++){cin>>a[i][j];} }for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){b[i][j]=-9999999;}}b[1][1]=a[1][1];for(int i=2;i<=n;i++){for(int j=1;j<=i;j++){b[i][j]=max(b[i-1][j],b[i-1][j-1])+a[i][j];}}int t=-1;for(int i=1;i<=n;i++){if(t<=b[n][i])t=b[n][i];}cout<<t;return 0;
}
继续看例题,上升序列,即在一串序列中找到最大的上升序列的长度。
问题描述
一个数的序列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
那自然也是两步,明确状态:dp[i]表示的是以i为终点上的最大上升序列长度
状态转移:以i为终点的最大长度是由什么转移的?显然是由它前面i-1个位子决定的,也就是以它为终点(自然有i)那么首先就要有(a[j]<a[i])的条件,如果满足,则以i为终点的也就是以j为终点加上i本身。最后对每个位子进行比较。
for(int i=1;i<=N;i++){for(int j=1;j<i;j++){if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1); }mymax=max(mymax,dp[i]);
}
还有经典的LCS问题
问题描述
输入两个字符串, 要你求出两个字符串的最长公共子序列长度。样例输入
abcfbc
abfcab
样例输出
4
并不一定要连续挑,而是从前面挑保证连续相同即可,可以跳着选。
定义状态dp[i][j]表示S1中前i个字符和S2中前j个字符时得到的最大的长度,当i=0或j=0时dp初始化为0
转移:首先i,j要相同,那么自然是它的前面的位子加1,如果不同,此时第i,j个位子自然是前面dp[i-1][j]和dp[i][j-1]中的较大值。即在这两种情况下不会因为你的i或者j增加了一个长度而发生改变。
int len1 = s1.length();int len2 = s2.length();for (int i = 0; i <= len1; i++)dp[i][0] = 0;for (int j = 0; j <= len2; j++)dp[0][j] = 0;for(int i=0;i<=len1;i++){for(int j=0;j<=len2;j++){if(s1[i]==s2[j])dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}
另一类经典的DP问题就是背包问题,详情见我之前的文章,这里就不多赘述了哈
把链接附上刷题之路:背包问题的探索_m0_52157848的博客-CSDN博客
刷题记录
1、印章
问题描述
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
“蓝桥杯”练习系统
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
double dp[30][30];//表示i张印章中有j种类型
int main(){int n,m;cin>>n>>m;double p=1.0/n;memset(dp,0,sizeof(dp));for(int i=1;i<=m;i++){//i张印章 for(int j=1;j<=n;j++){//j种印章 if(i<j)dp[i][j]=0;//购买数小于种类数不可能发生else if(j==1)dp[i][j]=pow(p,i)*n; //这里的意思不是说总共只有一种类型的印章,而是拿到手上只有一种类型印章的概率 else dp[i][j]=dp[i-1][j]*(1.0*j/n)+dp[i-1][j-1]*(1.0*(n-j+1)/n);}}printf("%.4lf",dp[m][n]);return 0;
}
2、拿金币
极其罕见能让我一遍过的题目,很适合刚接触dp的人做
问题描述
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
“蓝桥杯”练习系统
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
ll dp[1005][1005];
ll a[1005][1005];
int main(){ll n;scanf("%lld",&n);for(ll i=1;i<=n;i++){for(ll j=1;j<=n;j++){scanf("%lld",&a[i][j]);}}memset(dp,0,sizeof(dp));dp[1][1]=a[1][1];for(ll i=1;i<=n;i++){for(ll j=1;j<=n;j++){dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];}}printf("%lld",dp[n][n]);return 0;
}
[NOIP2002 普及组] 过河卒 - 洛谷
难在看出这题是用dp思想,在不考虑马存在的情况下,dp[i][j]=dp[i-1][j]+dp[i][j-1]
#include <stdio.h>
#include <iostream>
#include<algorithm>
#include<string.h>
#include <map>
using namespace std;
typedef long long ll;
ll dp[100][100];
ll p[100][100];
int main()
{int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;x1+=2; x2+=2; y1+=2; y2+=2;p[x2-1][y2-2]=1;p[x2-2][y2-1]=1;p[x2+1][y2+2]=1;p[x2+2][y2+1]=1;p[x2-1][y2+2]=1;p[x2-2][y2+1]=1;p[x2+1][y2-2]=1;p[x2+2][y2-1]=1;p[x2][y2]=1;memset(dp,0,sizeof(dp));int flag;dp[2][2]=1;for(int i=2;i<=x1;i++){for(int j=2;j<=y1;j++){if(p[i][j])continue;if(i==2&&j==2)continue;else dp[i][j]=dp[i-1][j]+dp[i][j-1];}}cout<<dp[x1][y1];return 0;
}
Problem - C - Codeforces
dp加上一个选择
#include <bits/stdc++.h>
using namespace std;
int dp[505][505];
int d[505],a[505];
int main(){int n,l,k;cin>>n>>l>>k;for(int i=1;i<=n;i++)cin>>d[i];for(int i=1;i<=n;i++)cin>>a[i];d[++n]=l;for(int i=0;i<=n;i++){for(int j=0;j<=n;j++){dp[i][j]=1e9+5;}}dp[1][1]=0;for(int i=2;i<=n;i++){for(int j=1;j<=i;j++){for(int k=1;k<i;k++){dp[i][j]=min(dp[i][j],dp[k][j-1]+(d[i]-d[k])*a[k]);}}}int temp=1e9;for(int i=0;i<=k;i++){temp=min(temp,dp[n][n-i]);}cout<<temp;return 0;
}
刷题之路:DP思想(动态规划)相关推荐
- 蓝桥杯备考-刷题之路-动态规划算法(DP算法)Part1
之前在刷力扣的时候就是浑浑噩噩的,照着评论区的答案写了一遍就万事大吉了,没有深度思考过.这次备考蓝桥杯看到DP算法的第一道题就不会,更难受的是看答案了依然完全不理解,所以决心把DP算法一次弄懂. 开始 ...
- 2021.5.21开始的兔系刷题之路 根据LeetCode分类进行逐个击破 培养出自己的套路~
十二月了 再更一波 最近的题解都写在这个仓库中,另外仓库中也记录了自己学习前端过程中的收获~ 近期刷题情况-- 2021-11突然好多人看这篇XD 来更一波,依旧在保持刷题啦~ 目前是跟着一本前端算法 ...
- LeetCode刷题笔记(算法思想 四)
LeetCode刷题笔记(算法思想 四) 七.动态规划 斐波那契数列 70. 爬楼梯 198. 打家劫舍 213. 打家劫舍 II 信件错排 母牛生产 矩阵路径 64. 最小路径和 62. 不同路径 ...
- 【Leetcode】刷题之路2(python)
哈希映射类题目(简单题小试牛刀啦bhn) 242.有效的字母异位词 349.两个数组的交集 1002.查找常用字符 202.快乐数 383.赎金信 242. 有效的字母异位词 用python的Coun ...
- 【Leetcode】 刷题之路1(python)
leetcode 刷题之路1(python) 看到有大佬总结了一些相关题目,想着先刷一类. 1.两数之和 15.三数之和 16.最接近的三数之和 11.盛最多的水 18.四数之和 454.四数相加II ...
- 判断输入的字符串是否为回文_刷题之路(九)--判断数字是否回文
Palindrome Number 问题简介:判断输入数字是否是回文,不是返回0,负数返回0 举例: 1: 输入: 121 输出: true 2: 输入: -121 输出: false 解释: 回文为 ...
- LeetCode 刷题之路(python版)
摘自:https://blog.csdn.net/qq_32384313/article/details/90745354 LeetCode 刷题之路(python版) 小坏wz 2019-06-02 ...
- 树的直径,树的最长路dp思想
dp一直弱死了,树型dp很多基本的题都不会,最近在刷树型dp的题,把关于树的最长路的思想总结一下: 树的直径:树中距离最远的两点间的距离. 下面说几道题: hdu 2196:对于树上(双向边)的每一个 ...
- (精)【ACM刷题之路】POJ题目详细多角度分类及推荐题目
POJ上的一些水题(可用来练手和增加自信) (poj3299,poj2159,poj2739,poj1083,poj2262,poj1503,poj3006,poj2255,poj3094) 初期: ...
最新文章
- zip4j实现多线程压缩
- mysql数据库改成支持中文_修改Mysql编码支持中文
- 第二十五期:搞定Linux Shell文本处理工具,看完这篇集锦就够了
- 二维分类教案_幼儿园中班数学教案中班数学教案二维排序——师乐汇幼儿教师教育网...
- Windows10上安装Kali并设置apt源
- web.config 加密/解密
- 移动端图片裁剪上传—jQuery.cropper.js
- awg线径与电流_AWG线规—导线外径(MM)与电流(A)对照
- TrustedInstaller权限的问题
- 毕业生见习期考核鉴定表(单位考核意见)
- Android开发高德地图定位中GPS坐标转换
- 《信息物理融合系统(CPS)设计、建模与仿真——基于 Ptolemy II 平台》——2.3 层次结构和复合角色...
- linux编程 —— shell编程脚本常用语法总结 【学习笔记】
- Linux 内核软死锁(soft lockup)记录
- 50M/s, Onedrive直链提取-IDM实现满速下载
- 电击手表设计#开源#
- iOS开发之移动应用海外推广平台资源总结
- 通过github构建个人博客1-基本环境搭建
- 手机品牌,型号,分辨率,系统,版本号
- messages报logger: Waiting minutes for filesystem containing crsctl
热门文章
- 主分区损坏diskgenius_用DiskGenius(原DiskMan)修复损坏的硬盘分区
- JBPM4.4学习使用
- 18在protel DXP中PCB图中给电路板绘制边框、安装孔的方法介绍成都电路板设计
- 工作不顺心怎么办?——Leo网上答疑(57)
- pygame教程实例(四)小球弹跳(事件)
- seo文章批量更新-SEO文章自动批量生成
- Arthas-thread命令定位线程死锁
- allegro如何导入嘉立创可以识别的bom
- Leetcode 1823 找出游戏的获胜者 (约瑟夫环问题)
- 一加7t人脸识别_一加7T深度测评:它体现了最佳的技术价值吗?