什么是动态规划
动态规划(Dynamic Programming DP) 是运筹学的一个分支,是求解决策过程最优化的过程。其在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果。
算法核心思想
- 通过拆分子问题,记忆状态,来减少重复计算,将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法,与分治算法类似。
- 与分治算法不同的是,动态规划问题,分解得到子问题往往不是互相独立的。 ( 即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解 )
问题引入
求斐波那契数列第 n 项的值
斐波那契数列
指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
第 i
项的值等于第 i-1
项加上第 i-2
项
Ⅰ.用已知的值求自己
int n, f[10086];
cin >> n;
f[0] = 0; f[1] = 1; //初始状态
for (int i = 2; i <= n; ++i)
f[i] = f[i-1] + f[i-2]; //状态转移
cout << f[n];
Ⅱ.用自己的值求未知
int n, f[10086];
cin >> n;
f[0] = 0; f[1] = 1; //初始状态
for (int i = 0; i < n; ++i)
f[i+1] += f[i]; f[i+2] += f[i]; //状态转移
cout << f[n];
Ⅱ.记忆化搜索
普通的搜索是这样的
在这样的情况下如果是多次询问 那么就会有很多次重复计算
int dfs(int i) { //求斐波那契数第 i 项的值
if (i == 0) return 0;
if (i == 1) return 1;
return dfs(i-2)+dfs(i-1);
}
这时候如果我们把已经算出来的值记录下来,那么就可以大大降低算法的时间复杂度
int dfs(int i) { //求斐波那契数第 i 项的值
if (i == 0) return 0;
if (i == 1) return 1;
if (g[i]) return f[i]; //g[i] 代表斐波那契数列第 i 项 有没有被算出来
g[i] = true;
f[i] = dfs(i-2) + dfs(i-1); //f[i] 代表斐波那契数量第 i 项
return f[i];
}
总结
/**********动态规划(DP)**********
* 用若干状态储存对应的值的转移方法
* 1.状态:刻化题目中在变化的量
* 2.初始化:变化的量最开始的情况
* SA:斐波那契数列中的 f[0] = 0 与 f[1] = 1
* 3.转移方程:描述这些量怎么变化
* SA:f[i] = f[i-1]+f[i-2]
*******************************/
例题
过河卒
题解
首先我先假设没有马的影响,那么卒组可以从当前点的左边或者上边走到该点,假设从原点走到该点左边点的路径条数是 x
,从原点走到该点上边点的路径条数是 y
,那么从原点走到该点的路径条数就是 x+y
我们设
f
(
i
,
j
)
f(i,j)
f(i,j) 为从原点走到当前点所需要的路径条数,那么我们可以得到:
f
(
i
,
j
)
=
f
(
i
−
1
,
j
)
+
f
(
i
,
j
−
1
)
f(i,j)=f(i−1,j)+f(i,j−1)
f(i,j)=f(i−1,j)+f(i,j−1)
所以这道题的最终答案是
f
(
n
,
m
)
f(n,m)
f(n,m)
接下来我们再将马放入进去,当我们遇到马的时候路径是无效的,所以我们只需要判断当前点为马可移动到的点时让
f
(
x
,
y
)
=
0
f(x,y) = 0
f(x,y)=0 即可。
#include "bits/stdc++.h"
using namespace std;
#define ll long long
#define maxn
inline int read() { //快读
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int mx[] = {-2, -1, 1, 2, 2, 1, -1, -2}; //表示马横向的移动范围
const int my[] = {1, 2, 2, 1, -1, -2, -2, -1}; //表示马纵向的移动范围
int n, m, a, b;
ll f[25][25]; //十年OI一场空,不开long long见祖宗
bool c[25][25]; //记录该点是否可以通过
int main(){
//freopen("","r",stdin);
//freopen("","w",stdout);
n = read()+2; m = read()+2; a = read()+2; b = read()+2;
memset(c,0,sizeof(c));
memset(f,0,sizeof(f));
f[2][1] = 1;
c[a][b] = 1; //初始化
for (int i = 0; i < 8; ++i)
c[a + mx[i]][b + my[i]] = 1; //马所控制的点均不可通过
for(int i = 2; i <= m; ++i) {
for(int j = 2; j <= n; ++j){
if(c[j][i]) continue;
f[j][i] = f[j - 1][i] + f[j][i - 1]; //状态转移
}
}
printf("%lld", f[n][m]);
return 0;
}