• 算法总结--动态规划


    声明(叠甲):鄙人水平有限,本文为作者的学习总结,仅供参考。


    1.动态规划介绍

    动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。其中每一个状态一定是由上一个状态推导出来,这是DP的一个重要标志。


    2.DP大法的使用

    一般的来说,使用DP大法一般有以下几个重要的步骤

    【1】 确定 DP 数组下标的含义
    【2】 根据题意推导出递归公式(状态转移方程)
    【3】 初始化 DP 数组,一般要进行边界处理,有时也会通过扩大数组的大小来避免边界的处理
    【4】 遍历 DP 数组,并进行对应的更新


    3.举些栗子

    3.1 线性 DP —— 斐波那契数

    题目如下:

    题目描述

    斐波那契数?(通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

    F(0) = 0F(1) = 1
    F(n) = F(n - 1) + F(n - 2),其中 n > 1
    

    给定 n ,请计算 F(n) 。

    根据题目描述显然这个问题可以使用 DP 大法进行求解,且已给出了其状态转移方程 F(n) = F(n - 1) + F(n - 2),以及对应的边界F(0) = 0,F(1)?= 1,于是我们就能够顺利写出对应的代码

    int fib(int n) 
    {
        if(n == 0) return 0;
        if(n == 1) return 1;
        int dp_n = 0;
        int dp_n_1 = 1;
        int dp_n_2 = 0;
        for(int i = 0;i < n-1;i++)
        {
            dp_n = dp_n_1 + dp_n_2;
            dp_n_2 = dp_n_1;
            dp_n_1 = dp_n;
        } 
        return dp_n;
    }
    

    3.2 二维 DP —— 过河卒

    题目描述

    棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

    棋盘用坐标表示,A 点 (0, 0)、B 点 (n, m),同样马的位置坐标是需要给出的。

    现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

    该题可以说是不同路径问题的变种,根据题目描述,我们可以设卒到(x,y)处的方案有dp[x][y]种,又卒只能向下、或者向右,故可以推出其状态转移方程为dp[x][y]=dp[x-1][y]+dp[x][y-1],接着是边界与特殊点(马的控制点)的处理,具体可以看代码的实现:

    #include h> 
    
    using namespace std;
    
    int main()
    {
    	int m,n,x_m,y_m;
    	cin >> n >> m >> x_m >> y_m;
    	long long int dp[n+1][m+1];
    	for(int i = 0;i <= n;i++)
    	{
    		for(int j = 0;j <= m;j++)
    		{
    			int d_m = (i-x_m)*(i-x_m) + (j-y_m)*(j-y_m);     // 卒到马距离的平方
    			if(d_m == 5 || d_m == 0) dp[i][j] = 0;
    			else if(i == 0 && j == 0) dp[i][j] = 1;          // 起点
    			else if(i == 0 && j != 0) dp[i][j] = dp[i][j-1]; // 上边界
    			else if(j == 0 && i != 0) dp[i][j] = dp[i-1][j]; // 左边界
    			else dp[i][j] = dp[i-1][j] + dp[i][j-1];         // 正常情况 
    		}
    	}
    	cout << dp[n][m] << endl;
    	return 0;
    } 
    

    3.2 四维 DP —— 方格取数

    题目描述

    设有 N 的方格图(N<9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0。如下图所示(见样例):

    A
     0  0  0  0  0  0  0  0
     0  0 13  0  0  6  0  0
     0  0  0  0  7  0  0  0
     0  0  0 14  0  0  0  0
     0 21  0  0  0  4  0  0
     0  0 15  0  0  0  0  0
     0 14  0  0  0  0  0  0
     0  0  0  0  0  0  0  0
                             B
    

    某人从图的左上角的 A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0)。
    此人从 A 点到 B 点共走两次,试找出 2 条这样的路径,使得取得的数之和为最大。

    该题的难点在于有两条路线同时进行,但是思考清楚后还是很简单的,按照 DP 大法的步骤来

    【1】 设两条路径分别到两点 (i,j)与(k,l)时的最大的数为 DP[i][j][k][l]
    【2】 根据观察可以推得状态转移方程为 DP[i][j][k][l] = max(a,b), 其中 a,b 如下:
    a = max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1])
    b = max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1])
    【3】 边界的处理,在此我们可以多申明出一个都为 0 的第 0 行与第 0 列


    代码实现如下:

    #include h> 
    
    using namespace std;
    
    int main()
    {
    	int nums[10][10] = {0,};
    	int dp[10][10][10][10] = {0,};
    	int n = 0;
    	cin >> n;
    	while(1)
    	{
    		int x,y,a;
    		cin >> x >> y >>a;
    		if(x == 0 && y == 0 && a == 0) break;
    		nums[x][y] = a;
    	}
    	for(int i = 1;i <= n;i++)
    	{
    		for(int j = 1;j <= n;j++)
    		{
    			for(int k = 1;k <= n;k++)
    			{
    				for(int l = 1;l <= n;l++)
    				{
    					int a = max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]);
    					int b = max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]);
    					dp[i][j][k][l] = max(a,b) + nums[i][j] + nums[k][l];
    					if(i == k && j == l) dp[i][j][k][l] -= nums[i][j];
    				}
    			}
    		}
    	}
    	cout << dp[n][n][n][n];
    	return 0;
    } 
    

    4.参考

    代码随想录
    4维DP例题讲解

    本文到此结束,希望对您有所帮助。


    __EOF__

  • 本文作者: luoke
  • 本文链接: https://www.cnblogs.com/luokeIT/p/17245274.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    高斯积分-Gaussian Quadrature
    Python入门到放弃
    Java的集合
    【运筹优化】运筹学导论:线性规划导论
    店铺进销存管理系统源码
    【JVM】JVM实战笔记-随笔
    外汇天眼:本周无牌裸奔平台名单出炉,你踩“坑”了么?!!
    PG-PFC-17E、PG-PEV-16A、PG-PEV-18A插装式比例流量阀控制器
    少走99%的弯路,告别Netflix组件,拥抱SpringCloudAlibaba(GitHub 99k starts一直火到头条)
    MyBatis:配置文件
  • 原文地址:https://www.cnblogs.com/luokeIT/p/17245274.html