动态规划入门
写一点我对动态规划的理解和相关题解
首先,我们来看看斐波那契数列 1,1,2,3,5…每一项是前两项的和,这是一个逐渐递增的数组;我们都知道它的算式是F(n)=F(n-1)+F(n-2);
输出斐波那契数列的前二十项
#include<iostream>
using namespace std;
int fib(int k)
{
if (k == 1)return 1;
if (k == 2)return 1;
else return fib(k - 1) + fib(k - 2);
}
int main()
{
for (int i = 1; i <= 20; i++)
cout << fib(i) << endl;
return 0;
}
其实这里的第n项是由前两项(n-1项和n-2项)加和得到的,由此可知,第三项是由第一项和第二项得到的。这里也就是递归的结束点,我们需要一个初始值让递归返还数值,这里的初始值为斐波那契数列的第一二项,由此计算出前二十项。现在再来看下一题;
跳台阶问题
有N级的台阶,你一开始在底部,每次可以向上迈最多2级台阶(最少1级),问到达第N级台阶有多少种不同方式?
如果你想跳上第三级台阶,那么你必须跳上第一级台阶在跳上第三级,或者跳上第二级在跳上第三级,那么就有2种方式;
同理,跳上第四级台阶,你就需要跳上第三级或者直接从第二级跳上;那么跳上第四级台阶的方法就转换成了跳上第二级台阶和跳上第三级台阶方法的和;跳上第三级台阶的方法有多少呢?我们之前已经计算过了,是两种+第二级(1种)=3种;
那么第n级台阶就等于第n-1级加第n-2级;F(n)=F(n-1)+F(n-2)这和斐波那契数列是一样的;显然,这个算式是我们解题的关键。我们称它为**“状态转移方程”**
状态转移就是dp的思想;如果想通了,你可以去做一做它的原题
(改编自洛谷台阶https://www.luogu.com.cn/problem/P1192)
第三题我们将一维dp拓展到二维
方格上的路径
对于这题我们知道最短路径的数量(M+N) * (M+N-1) * …(N+1)/M!但是如果用这个算式计算,会非常慢,那么就需要用到dp;这里我们选择初始化第0行和第0列,我们需要用数据填满这个表格,从第一行第一列开始,表格的每一个数字都有其左边和上边的数字得到,那么不难得出状态转移方程 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
代码
using namespace std;
long long dp[101][101];
int n, m;
int main()
{
int t;
cin >> t;
while (t--)
{
cin >> n >> m;
for (int i = 0; i <= n; i++)
dp[i][0]=1;
for (int i = 0; i <= m; i++)
dp[0][i] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
cout << dp[n][m]<<endl;
}
return 0;
}
加点难度,我们在这个表格上加入一些限制
洛谷过河卒
https://www.luogu.com.cn/problem/P1002
很显然状态转移方程依旧为dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
这题在对于数据的初始化上挖了坑,由此可见,解决dp问题时,数据的初始化也是不能忽视的;如下图情况:
不能全部初始化为1;由于只能向下或者向右第一行第一列马能跳到的地方后面自然也是不能走到的,需要初始化为0;
这题需要使用long long,数据会很大;
代码
#include<iostream>
using namespace std;
long long dp[25][25];
bool book[25][25];
int n, m;
int hx, hy;
int dir[8][2] = { {2,-1},{2,1},{1,-2},{1,2},{-1,2},{-1,-2},{-2,-1},{-2,1} };
int main()
{
cin >> n >> m;
cin >> hx >> hy;
for (int k = 0; k < 8; k++)
{
int hxx = hx + dir[k][0];
int hyy = hy + dir[k][1];
if (hxx >= 0 && hyy >= 0 && hxx <= n && hyy <= m)
book[hxx][hyy] = 1;
}
book[hx][hy] = 1;
for (int k = 0; k <= m; k++)
if (!book[0][k])
dp[0][k] = 1;
else break;//之后的全部为0,不能少
for (int k = 0; k <= n; k++)
if (!book[k][0])
dp[k][0] = 1;
else break;//同上
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (!book[i][j])
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
cout << dp[n][m];
return 0;
}
之后我们来看一道经典dp问题
数字三角形,又被称为树塔,需要我们查找最大路径,由于是查找最大的路径,我们就需要选择大的值向下转移;
值得注意的是这次的最大值并不在最后一个取得;
代码
#include<iostream>
using namespace std;
int map[1001][1001];
int dp[1001][1001];
int maxx = -1;
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
for(int j=1;j<=i;j++)
cin >> map[i][j];
dp[1][1] = map[1][1];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + map[i][j];
if (dp[i][j] >= maxx)maxx = dp[i][j];
}
cout << maxx;
return 0;
}
这些都是一些动态规划相对入门的基础题,深入学习动态规划还有很长一段路要走,希望能够帮助你。