动态规划(寒假版一之递推)

2024-04-23 21:08

本文主要是介绍动态规划(寒假版一之递推),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

https://blog.csdn.net/qq_32400847/article/details/51148917#commentsedit

https://blog.csdn.net/u011815404/article/details/79703231

https://blog.csdn.net/mmc2015/article/details/73558346

很好的三篇详细讲解动态规划的博客,大家可以学学。

动态规划有的时候总是和递推公式和状态转移方程缠在一起,他们之间有着紧密的联系,DP往往都是这样的解法。


多阶段决策问题:若一类问题的求解过程可分为若干个互相联系的阶段,在每一个阶段都需作出决策,并影响到下一个阶段的决策。这类问题的解决,就是要在可以选择的那些策略间,选一个最优策略,使在预定的标准下达到最好的效果。
    阶段:将所给求解问题的过程恰当地分成若干个相互联系的阶段,以便于求解,过程不同,阶段数就可能不同,描述阶段的变量称“阶段变量”。
    状态:描述事物的性质,不同事物有不同的性质,因而用不同的状态来刻画。对问题的求解状态的描述是分阶段的。描述状态的量称“状态变量”
    决策:一个阶段的状态给定以后,从该状态演变到下一阶段某个状态的选择性操作。描述决策的变量称决策变量。决策变量的范围称“允许决策集合”。
    无后效性:我们要求状态具有下面的性质:如果给定某一阶段的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响,所有各阶段都确定时,整个过程也就确定了。换句话说,过程的每一次实现可以用一个状态序列表示,这个性质称为“无后效性”。
    策略:由每个阶段的决策组成的序列称为策略。对于每一个实际的多阶段决策过程,可供选取的策略有一定的范围限制,这个范围称“允许策略集合”。允许策略集合中达到最优效果的策略称“最优策略”。
    状态转移方程:用数学公式描述与阶段相关的状态间的演变规律。是本阶段的状态往往是上一阶段状态和上一阶段决策的结果。如果给定了第K阶段的状态Sk以及决策uk(Sk),则第K+1阶段的状态Sk+1也就完全确定。

状态转移方程还是比较难想到的。

下面 看看各个题型:

1.递推:其实就是简单的状态转移,找到其中的规律,写出递推公式,这道题也就AC了,没有什么技术含量,纯属学学DP的状态转移并知道是个什么意思。

Recursion Practice HDU上的递推题目,可以了解下状态转移。

放苹果 POJ 1664 

Description

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

Input

第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。

Output

对输入的每组数据M和N,用一行输出相应的K。

Sample Input

1
7 3

Sample Output

8

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
const int maxn=1000100;
using namespace std;
typedef long long ll;
const int mod=1000000000;ll fun(ll m,ll n)
{if(m==0||n==1)   //这个就是 1.只有一个盘子时,那就只能放在这里面了,返回1。//当苹果没了的时候,就说明分好了(可以仔细想想)。	return 1;if(m<n)fun(m,m);     elsereturn fun(m,n-1)+fun(m-n,n);  //这里只是 很大的两种情况 ,不是所有盘子都有苹果,和所有盘子都有苹果 //这里就是将问题 变为更小的子问题,现在可以影响之后的状态.//在找到方法后,就是把他们化为更小的子问题,去解决问题。//直到(化到最小时)最后时,有个结束条件,根据这个 回溯,求出答案。   
}int main()
{int t;cin>>t;ll m,n;while(t--){cin>>m>>n;cout<<fun(m,n)<<endl;}return 0;
}

这个题  很基础 ,入门题 。就不说了。

例题 2.

下面看一道 相对难一点的递推题(骨牌铺地砖加强版)

HDU 1143 

你能用多少种方法用2x1多米诺骨牌平铺一个3xn的矩形?这是3x12矩形的瓷砖示例。 

输入

由几个测试用例组成,后跟一行包含-1。每个测试用例都是一行,包含一个0≤n≤30的整数。

输出       

对于每个测试用例,输出一个整数,给出可能的瓷砖数量。 

sample input

2

8

12

-1

sample output

3

153

2131

其实这个题,规律不是很好找。 你首先得把这个问题简单化,才能找出其中规律。接下来看看是如何进行简单化和找出最有子结构。

我们可以假设这个图形 的列数为 i,当i 为奇数的时候,肯定是不能放得下 1*2 的骨牌的。为偶数时,才可以放的下。这样我们就只用考虑 i 为偶数的情况。

(图画的不好)从这张图中,你可以看到什么规律?大体上可以粗俗的分为4 种 图案:

在分的仔细一点的话,就是上面的 1 2 3 ,就可以分为一种,因为他们的基本组成单位(可以想象到化学的晶胞(就是最简单的一个单元,不能再分了)) 的列数都是 2; 而下面的这个的 列数 可以是 4 6 8 10 ...2k ,但是他们的基本组成单元 就是:

你也可以往后面继续画列数 是 4 6 8 10 的图形。现在的话 ,分出来了最优的子结构,现在就可以想状态转移方程是什么了?

首先,这类题基本上都是先打一个表,根据需要可以开一维和二维的数组。这个提就可以开一维 数组。每个元素表示的是当前的方法数。

第一种情况就是 分为 i-2 列图形 和一个 2列的图形,方法数就是  i-2 列方法数就是f[i-2] ,这个2 列的方法数就是 3了,所以此时的方法数就是f[i-2]*3; 这个就相当于 将一个复杂的图形化简单了,为什么这样想呢?在一个 i-2列图形 的后面 加上2列的这个图形,就会变成 i 列的。  (就相当于拆开了,计算更加简单),这种情况的 fn1=f[i-2]*3;

第二种情况就是 想法也是跟第一种情况一样的,也是拆开想这种方法,具体我就不说了。但是有一点区别就是,这个图形不是一个完整的个体,不想一情况那样,这个图形还得先补全,可以补成列数是 4 的,6的 8 的,2k的,这个图形的方法数是2,上面的两个横着的可以放在下面,就是两种,他们的 方法数依次是f[i-4]*2  f[i-6]*2 f[i-8]*2 ... ,他们全加起来 就是fn2=f[i-4]*2+f[i-6]*2+f[i-8]*2+....+f[0]*2.

综上所述,状态转移方程就出来了(更像一个规律方程)

初始方法数 f[0]=1; f[2]=3;

f[n] = f1[n] + f2[n]

f[n] = 3*f[n-2] + 2*f[n-4] + ... + f[2]*2 + f[0]*2; ----- ①

f[n-2]=   0     + 3*f[n-4] + ....+ f[2]*2+ f[0]*2; ----- ②

 f[n]= ① - ② = 4*f[n-2] - f[n-4].

于是就AC了。

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
const int maxn=1000100;
using namespace std;
typedef long long ll;
const int mod=1000000000;
int main()
{ll i;ll a[31];a[0]=1;a[2]=3;for(i=4;i<31;i=i+2)a[i]=4*a[i-2]-a[i-4];ll num;while(cin>>num){if(num==-1)break;if(num&1)cout<<"0"<<endl;else		cout<<a[num]<<endl;}return 0;
} 

 

看看 递推 相关的题目,有难度,比一般的题目高好多等级(有一些是大数模拟的)(大数乘法和除法),这些题仅仅是递推的。

        Tri Tiling                                                
        Computer Transformation                                
        Train Problem II                                          
        How Many Trees?                                           
        Buy the Ticket                                     
        Game of Connections                             
        Count the Trees                                 
        Circle                                         
        Combinations, Once Again                        
        Closing Ceremony of Sunny Cup                        
        Rooted Trees Problem                              
        Water Treatment Plants                                  
        One Person                                     
        Relax! It’s just a game                      
        N Knight                                        

既然用到了 大数 乘法 ,我就提一嘴吧 因为有很多的打表数据量都是非常大的上百位的数据,除非你会 java bignum,大数乘法和除法都是用二维的一个整形数组来模拟的,其中也有很多技巧性:

例题:Train Problem II HDU 1023

问题描述            

我们都知道火车问题一,伊格纳提乌斯火车站的老板想知道,如果所有的火车都严格按照递增的顺序来,那么所有的火车能从火车上下多少个订单。        

输入            

输入包含几个测试用例。每个测试用例由一个数字n(1<=n<=100)组成。输入被文件结尾终止。              

产量              

对于每个测试用例,您应该输出所有列车可以通过多少种方式离开铁路。

sample input 

1

2

10

sample output

1

2

5

16796

首先先说说状态转移方程,这是一个卡特兰数,公式为c(n)=c(n-1)*(4n-2)/(n+1) 依据这个公式,再根据大数来做。

直接上代码吧:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int a[110][100];
void ktl()
{a[1][0] = 1;a[2][0] = 1;a[1][1] = 1;a[2][1] = 2;int yu, temp, length = 1;				//length 表示当前数字的长度for (int i = 3; i <= 100; i++){//这是个 大数乘法yu = 0;for (int j = 1; j <= length; j++){temp = (a[i - 1][j] * (4 * i - 2)) + yu;a[i][j] = temp % 10;yu = temp / 10;}while (yu != 0){a[i][++length] = yu % 10;yu /= 10;}//------------------------------------------//下面是大数除法for (int j = length; j >= 1; j--){temp = a[i][j] + yu * 10;a[i][j] = temp / (i + 1);yu = temp % (i + 1);}while (!a[i][length])length--;a[i][0] = length;//---------------------------------------------}
}
int main()
{ktl();int n;while (cin>>n){for (int i = a[n][0]; i >= 1; i--)printf("%d", a[n][i]);cout << endl;}return 0;
}

这个算法是用了个 二维的数组,行就代表了几辆火车 的方法数,列的首个元素 是当前 值的位数(因为要大数模拟,要把数字一位一位的分开,就需要把位数确定下来),后面 几位 就是各个位的值,因为乘法是从最末尾开始乘起,每次都要看看是否进位。首先初始条件为 一辆火车 的时候就是 1种,位数是1 ,于是a[1][0]=1,a[1][1]=1;    两辆的时候a[2][0]=1,a[2][1]=2; 接下来就是大数乘法的操作。

       //这是个 大数乘法
yu = 0;
for (int j = 1; j <= length; j++)
{temp = (a[i - 1][j] * (4 * i - 2)) + yu;a[i][j] = temp % 10;yu = temp / 10;
}
while (yu != 0)
{a[i][++length] = yu % 10;yu /= 10;
}

 乘法会有进位的,基本上与大数加法一样,具体就不详讲了。

除法是正向从第一位开始除的:

        //下面是大数除法
for (int j = length; j >= 1; j--)
{temp = a[i][j] + yu * 10;a[i][j] = temp / (i + 1);yu = temp % (i + 1);
}
while (!a[i][length])length--;
a[i][0] = length;//---------------------------------------------

如果除不尽的 话,还要把余数保存起来,加到下一次的计算中,就这样一直除,最后有消去前导0 的操作。是 0 位数就减少1 。整体二维数组不好理解,其他操作还很平常。

1  1
2  2
3  5
4  14
5  42
6  132
7  429
8  1430
9  4862
10  16796
11  58786
12  208012
13  742900
14  2674440
15  9694845
16  35357670
17  129644790
18  477638700
19  1767263190
20  6564120420
21  24466267020
22  91482563640
23  343059613650
24  1289904147324
25  4861946401452
26  18367353072152
27  69533550916004
28  263747951750360
29  1002242216651368
30  3814986502092304
31  14544636039226909
32  55534064877048198
33  212336130412243110
34  812944042149730764

每一个 0位置 i火车方法数的位数总和  ,后面 位数倒过来就是值,比对一下。(好妙)

卡特兰数时用的题型(具体的自己看吧):

1.出栈种类数量

2.将多边行划分为三角形问题

3..给顶节点组成二叉树的问题

4.括号化问题

说这么多,应该了解了吧,还不的话,那就多练练 。

这篇关于动态规划(寒假版一之递推)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/929890

相关文章

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

前端 CSS 动态设置样式::class、:style 等技巧(推荐)

《前端CSS动态设置样式::class、:style等技巧(推荐)》:本文主要介绍了Vue.js中动态绑定类名和内联样式的两种方法:对象语法和数组语法,通过对象语法,可以根据条件动态切换类名或样式;通过数组语法,可以同时绑定多个类名或样式,此外,还可以结合计算属性来生成复杂的类名或样式对象,详细内容请阅读本文,希望能对你有所帮助...

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插