动态规划求解收集硬币(‘超’详细介绍)
问题引入:
在m*n的方格中放有一些硬币,每格的硬币数目最多为一个,从方格左上方开始收集尽可能多的硬币并把它们带到右下方的单元格。每一步只可以从当前的位置向右移动一格或向下移动一格。当遇到一个有硬币的单元格时,就会将这枚硬币收集起来。问怎样移动才能收集到最多的硬币?
我们来举个例子分析分析:
如果硬币分布情况如图:
简化分布后(有硬币为 ‘1’,无硬币 ‘0’):
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 1 | 0 |
2 | 0 | 1 | 0 | 1 | 0 | 0 |
3 | 0 | 0 | 0 | 1 | 0 | 1 |
4 | 0 | 0 | 1 | 0 | 0 | 1 |
5 | 1 | 0 | 0 | 0 | 1 | 0 |
思路及分析:
我们假设 F(i,j)为走到(a,b)所能收集到的最大硬币数。单元格(a,b)可以经由上方(a-1,b)和左侧单元格(a,b-1)到达。单元格(a-1,b)对应的最大硬币数为 F(a-1,b),单元格(a,b-1)对应的最大硬币为 F(a,b-1)。
关键方程:
F(i,j)=Max{ F(i-1),F(i,j-1)} + arr[ i ][ j ]
注:
(1) Max 为自定义函数,用来选取较大的值。
(2) arr[ i ][ j ] 表示第 i 行,第 j 列是否有硬币,有则为 ‘1’ ,否则为 ‘0’。
利用上述公式,我们可以逐行或则逐列对 m*n 的表进行填充。
F(i,j)的情况如下:
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 1 | 1 |
2 | 0 | 1 | 1 | 2 | 2 | 2 |
3 | 0 | 1 | 1 | 3 | 3 | 4 |
4 | 0 | 1 | 2 | 3 | 3 | 5 |
5 | 1 | 1 | 2 | 3 | 4 | 5 |
注:填写规则:
以图中所标记的 ‘3’ 为例,在只能往右和往下走的情况下,选取 ‘3’ 上面和左面相邻的方格中最大的一个,如图中选取上面的 ‘2’ ,由于在第 3 行,第 4 列的位置有一个硬币,所以上面的 ‘2’ 需要加 ‘1’ ( arr[ 3 ][ 4 ] = 1 ),以此类推,课填充所有表格。
结合以上分析写出关键代码:
void FindMax() //m 代表行, n 代表列
{
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
F[i][j]=Max(F[i-1][j],F[i][j-1])+arr[i][j];
//计算出第i行,第j列的表单值。
}
}
printf("\n最多搜集 %d 个硬币。\n",F[m][n]);
//注意,输出并不是F(i,j)!!!而是表单上位于第m行,第n列的值!!!
}
回溯法的思路及分析:
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。
{个人理解:知道结果,倒推路径。(该题从终点倒推回起点,并记录路径。)}
通过回溯法我们可以有以下的判断:
(1):F(i-1,j)>F(i,j-1)时,到达单元格(i&#x