「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」
一、动态规划
首先动态规划要满足三个条件:
- 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
- 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
- 有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
动规解题的一般思路:
动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。
动态规划决策过程:
- 划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
- 确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
- 确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。
- 寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。 一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。
1、新21点
题目:爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏,描述如下:
爱丽丝以 0 分开始,并在她的得分少于 K 分时抽取数字。 抽取时,她从 [1, W] 的范围中随机获得一个整数作为分数进行累计,其中 W 是整数。 每次抽取都是独立的,其结果具有相同的概率。
当爱丽丝获得不少于 K 分时,她就停止抽取数字。 爱丽丝的分数不超过 N 的概率是多少?
题解:
- 动态规划题目,先定下 dp 的概念。令 dp[x]表示从得分为 x 的情况开始游戏并且获胜的概率,目标是求 dp[0]的值。
- 要想获胜则要符合
K <= x <= N,此时的概览 dp[x] = 1; - 计算
0<= x < K。注意到每次在范围 [1, W] 内随机抽取一个整数,所以求dp[x]概率时,分母为W,分子为所有胜利的次数,dp[x+1]+dp[x+2]+...+dp[x+W],因此可以得到如下状态转移方程:(dp[x+1]+dp[x+2]+...+dp[x+W])/W。
代码:
var new21Game = function(N, K, W) {
if(K === 0) return 1.0;
const dp = new Array(K + W).fill(0); // 初始置概率为0
for (let i = K; i <= N; i++) { // 置符合`K <= x <= N` 时,概率为1
dp[i] = 1;
}
for (let i = K - 1; i >= 0; i--) {
for (let j = 1; j <= W; j++) {
dp[i] += dp[i + j] / W;
}
}
return dp[0]
};
优化:
对 dp 的相邻项计算差分,有如下结果:dp[x] - dp[x+1] = (dp[x+1]-dp[x+W+1])/W,则
dp[x] = (dp[x+1]-dp[x+W+1])/W + dp[x+1]
dp[x] = (dp[x+1]+dp[x+2]+...+dp[x+W])/W
dp[x+1] = (dp[x+1+1]+dp[x+2-1]+...+dp[x+W+1])/W
dp[x] - dp[x+1] = (dp[x+1]-dp[x+W+1])/W
dp[x] = (dp[x+1]-dp[x+W+1])/W + dp[x+1]
var new21Game = function(N, K, W) {
if(K === 0) return 1.0;
const dp = new Array(K + W).fill(0);
for (let i = K; i <= N; i++) {
dp[i] = 1.0;
}
dp[K - 1] = 1.0 * Math.min(N - K + 1, W) / W;
for (let i = K - 2; i >= 0; i--) {
dp[i] = dp[i + 1] - (dp[i + W + 1] - dp[i + 1]) / W;
}
return dp[0]
};