初探动态规划(DP)

学习qzz的命名,来写一篇关于动态规划(dp)的入门博客。

动态规划应该算是一个入门oier的坑,动态规划的抽象即神奇之处,让很多萌新 萌比。

写这篇博客的目标,就是想要用一些容易理解的方式,讲解入门动态规划的真正意义。

奶萌兔的温馨提示:建议先理解dfs哦~(本文以一种较为新奇的方式解释DP)

动态规划

那什么是动态规划?

来问问神奇的奶萌兔吧(强行盗梗)!

(奶萌兔来给你讲解啦~虽然还在睡觉=w=)

动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题[1]和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。
通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。
这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。----来自wiki百科

说这么多,DP能干什么?

奶萌兔的回答:解决一些问题...解决一些,dfs效率无法通过的问题。

不如先讲一道例题:

例1

  爬楼梯:现在有n层楼梯,奶萌兔位于第0层 现在奶萌兔每一次可以往上爬1层或者2层,问奶萌兔爬到第n层有多少种方案?

这是一道经典的入门题,对于这道题,我们由简入深。

  如何dfs?

  这样考虑 dfs(int dep) 表示奶萌兔在第dep层。

  那有两个走法,一个是dfs(dep+1) 一个是dfs(dep+2)。

  而dfs的边界呢? 则是 n 。 即 if (dep>n) return;

  初始值? dfs(0) 从第0层开始。

  对于答案的统计? 那就是每当dep为n时,说明到达了对ans统计加1。 即 if (dep==n) ans++ 且return;

  说了怎么多dfs?对dp有个什么用啊!!!

  这时候,奶萌兔来引入一个东西——状态。

  状态是什么?用浅显易懂的解释就是,dfs需要保存的东西(不一定...),如dfs(int dep)  dep(奶萌兔当前的层数)就是一个状态。

  而dp则是利用状态之间的相互关联,答案得以更新。

  那下面,来讲 如何dp?

  这样考虑 DP[dep] 表示奶萌兔从第0层走到第dep层时的方案数

  依旧有两个走法,一个是DP[dep+1] 一个是DP[dep+2]。

  是不是和dfs很相似? 接下来是重点。

  初始值 dp[0]=1 到第0层有一种方案,就是一开始就在0层。

  DP[dep+1] DP[dep+2]会等于什么?

  DP[dep+1]+=DP[dep] DP[dep+2]+=DP[dep]

  因为走到第dep层的方案是DP[dep],那么在走一层的时候,就只能走到dep+1的地方,那么dep上的全部方案,在通过走一步这样唯一的一个方法,可以到达dep+1,所以走到dep+1包含了dep上的方案,dep+2同理。

  上面的两个式子就是状态转移方程了。

  简单的转化一下, dp[dep+1]+=dp[dep] dp[dep+2]+=dep[dep] 则dp[dep]=dp[dep-1]+dp[dep-2] (当然不转化也是可以的,但对于部分题目,一些转化之后可能可以因此优化效率)

例2

  从一个一维的题目,试试跳到二维。

  骑士遍历:第一问可以直接往字典序最小去dfs,所以直接关注第二问吧

第二问:给一个n*m的棋盘,给定起点x1,y1 终点 x2,y2 在只能走日且只能向右走的情况下,问方案数。

这是例1的拓展,从一维的向上走,变为二维的向右走。

首先对于这样的棋盘,我们是不习惯的,因为与我们的数组顺序不同,让人烦心。

那怎么办捏?问问神奇的奶萌兔!

奶萌兔幽幽的说了一句:对~称~旋~转~

 哈!这是原来的题

哈!这还是原来的题

于是旋转一下之后,棋盘还是原来的棋盘,只是方向改变了。

题目就变成了只走日字且向下走的情况。

  老规矩呢,如何dfs?

  用用新名词,状态,dfs的状态是什么? dfs(int x,int y) 表示当前奶萌兔骑士在(x,y)的位置。

  那就是有四个走法

  dfs(x+1,y+2)  dfs(x+2,y+1)   dfs(x+1,y-2) dfs(x+2,y-1)

  边界 0 ,n    0 ,m 即 x>0且x<=n    y>0且y<=m

  初始:dfs(x1,y1)

  答案的统计:每当 x==x2&&y==y2 时说明到终点了,那ans++

  dp类比就好啦,状态就是数组的下标。

  DP[x][y] 表示奶萌兔骑士从(x1,y1)出发到(x,y) 的位置时的方案。

  一样四个走法

  DP[x+1][y+2] DP[x+2][y+1]  DP[x+1][y-2] DP[x+2][y-1]

  边界是一样的哦~

  初始:DP[x1][y1]=1 (在起点的时候就是一个方案)

  答案:DP[x2][y2]

  那转移方程,和例1一样哦,只是拓展为二维,走法变多一些。

  DP[x+1][y+2]+=DP[x][y]     DP[x+2][y+1]+=DP[x][y]    DP[x+1][y-2]+=DP[x][y]     DP[x+2][y-1]+=DP[x][y]

  简单转化一下: dp[x][y]=dp[x-1][y-2]+dp[x-2][y-1]+dp[x-1][y+2]+dp[x-2][y+1];

  好了问题来了:怎么转化的? 不妨这样撕烤,固定住(x,y) 找找哪些点能到(x,y) 那些点的方案数加起来就是了(或者,对前面的方程变化,x+d改为x-d,y+d改为y-d  (d为常数))。

 1 #define ll long long
 2 #include<cstdio>
 3 #include<iostream>
 4 using namespace std;
 5 ll f[100][100];
 6 int ansx[100],ansy[100];
 7 int n,m,x1,x2,y1,y2;
 8 const ll HR=1e18;
 9 void dfs(int dep,int nowx,int nowy){
10     ansx[dep]=nowx;
11     ansy[dep]=nowy;
12     if (nowx==n&&nowy==m) {
13         for (int i=1;i<dep;i++)
14             printf("(%d,%d)-",ansx[i],ansy[i]);
15         printf("(%d,%d)",ansx[dep],ansy[dep]);
16         exit(0);
17     }
18         //dfs的顺序注意按字典序最小。
19     if (nowx+1<=n&&nowy-2>0) dfs(dep+1,nowx+1,nowy-2);
20     if (nowx+1<=n&&nowy+2<=m) dfs(dep+1,nowx+1,nowy+2);
21     if (nowx+2<=n&&nowy-1>0) dfs(dep+1,nowx+2,nowy-1);
22     if (nowx+2<=n&&nowy+1<=m) dfs(dep+1,nowx+2,nowy+1);
23 }
24 int main(){
25     scanf("%d%d%d%d%d%d",&n,&m,&x1,&y1,&x2,&y2);
26     if (x1==0) {
27         dfs(1,1,1); //第一问
28         printf("NO");
29         return 0;
30     }
31         //第二问
32     f[x1][y1]=1;
33     for (int i=x1+1;i<=x2;i++){ //从x1+1开始,因为x1这一行是不可能更新的。
34         for (int j=1;j<=m;j++){
35             if (j-2>0) f[i][j]+=f[i-1][j-2]; //边界
36             f[i][j]%=HR;
37             f[i][j]+=f[i-1][j+2];
38                         //为什么这个不判?原因是f[i-1][j+2]的值一定是0,因为无法更新到边界外的数,而初始值都是0,注意把数组开大即可,下面同理
39             f[i][j]%=HR;
40             f[i][j]+=f[i-2][j-1];
41             f[i][j]%=HR;
42             f[i][j]+=f[i-2][j+1];
43             f[i][j]%=HR;
44         }
45     }
46     printf("%lld",f[x2][y2]);
47     return 0;
48
49 }        

例2

以上就是入门的方案数dp。

后期可能更新其他dp呀~

原文地址:https://www.cnblogs.com/Bunnycxk/p/9265901.html

时间: 2024-11-10 08:17:28

初探动态规划(DP)的相关文章

Fibonacci斐波拉契数列----------动态规划DP

n==10 20 30 40 50 46 体验一下,感受一下,运行时间 #include <stdio.h>int fib(int n){ if (n<=1)     return 1; else            return fib(n-1)+fib(n-2); }int main( ){ int n; scanf("%d",&n); printf("%d\n" ,fib(n) );} 先 n==10 20 30 40 50 46

动态规划(DP),类似LIS,FatMouse&#39;s Speed

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1108 解题报告: 1.首先按照weight从小到大排列,weight相同的按照speed从大到小排列; 2.Count[i]表示到第i个老鼠时,所求的最长“速度递减”子序列的长度: 3.path[i]=j是题目的关键,记录在Count[i]=Count[j]时,即最长“速度递减”子序列最后一个老鼠的前一只老鼠的位置 4.递归输出id void output(in

(RQoj 15 采药------rwkj 10.1.5.253 1447) 动态规划 DP 1

#include <iostream>#include <string.h>using namespace std;int dp[105][1005], w[105],v[105],T,M;int max(int x,int y){ return x>y?x:y; } void f( ){ int i,j; for (i=1; i<=M; i++) for (j=0;j<=T; j++) { if (i==0) dp[i][j]=0; else dp[i][j]=

(RQoj 15 采药------rwkj 10.1.5.253 1447) 动态规划 DP 2

70 371 10069 11 2 #include <iostream>#include <string.h>using namespace std;int dp[105][1005], w[105],v[105],T,M;int max(int x,int y){ return x>y?x:y; }void f( ){ int i,j; for (i=M; i>=1; i--) for (j=0;j<=T; j++) { if (i==M+1) dp[i][j

(RQoj 15 采药------rwkj 10.1.5.253 1447) 动态规划 DP 3

#include <iostream>#include <string.h>using namespace std;int dp[1005], w[105],v[105],T,M;int max(int x,int y) { return x>y?x:y; }void f( ){ int i,j; for (i=1; i<=M; i++) for (j=T;j>=0; j--) if (j>=w[i]) dp[j]=max(dp[j],dp[j-w[i]]+

动态规划 DP

动态规划 DP 我们用f[ i ] 表示从 i 点出发到达终点的最多能休息的时间 然后我们发现 状态转移方程f[ i ] = f[ i+1 ] +1 ; 当该点 并没有工作计划时 f[ i ] = max(f[ i+len ],f[ i ]); 当该点 有工作计划时 一个或若干个 1 #include <bits/stdc++.h> 2 #define For(i,j,k) for(int i=j;i<=k;i++) 3 using namespace std ; 4 5 const i

poj 1458 动态规划DP

//  poj 1458  zoj 1733  最长公共子序列  DP #include <iostream>#include <string.h>#define N 1005using namespace std ;char  s1[N],s2[N];   int dp[N][N];int max(int a,int b)   {    return a>b ? a : b ;  }void f(int n,int m){   int i,j;    for (i=0; i

ppt Fibonacii数列的第n项------动态规划DP

#include <stdio.h>#define MAX 50+1int fib(int n){ int i,a[MAX]; a[1]=a[2]=1; for (i=3; i<=n; i++)               a[i]=a[i-1]+a[i-2];          return a[n];}void main( ){ int n; scanf("%d",&n); printf("%d\n" ,fib( n ) );} ppt

hdu2571 命运 动态规划Dp

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2571 Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了! 可谁能想到,yifenfei在斩杀了一些虾兵蟹将后,却再次面临命运大迷宫的考验,这是魔王lemon设下的又一个机关.要知道,不论何人,若在迷宫中被困1小时以上,则必死无疑! 可怜的yifenfei为了去救MM,义无返顾地跳进了