Leetcode 64.最小路径和(动态规划)(动态数组解决)

Leetcode 64.最小路径和(动态规划)(动态数组解决)

题目分析

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。
示例:
输入:
3 3
1 3 1
1 5 1
4 2 1
输出:
7

图片
图片均来自来自Leetcode题解

leetcode

解题思路

***** 关于动态规划的问题有部分内容引自Hollis Chuang的这篇文章 ****

由题目要求我们要找出路径的最小值,但是遍历太过庞大,我们很容易可以想到用动态规划解题:

动态规划的一般步骤

  1. 确定定义数组的意义
  2. 根据递归的思想确立递推关系式
  3. 确定初始值
  4. 利用初始值与递推关系式正向求目标结果;

1.确定定义数组的意义:

根据题目要求,我们的目标是求出最小路径的数字和,那么我们定义的数组的含义就是到m,n的最小路径的数字和。
dp[m][n]就代表了从坐标原点到m,n的最小路径的数字和
但是实际上的m,n为m-1,n-1。因为坐标原点为0,0;

2.根据递归思想确定递推关系式

从结果来看,我们要想办法将dp[x][y]与前几项相联系起来,倒过来走,我们可以发现只可以从上方或者下方到达dp[x][y],我们要求最小值,那么便可以得出

dp[x][y]=min(dp[x][y-1],dp[x-1][y])+num[x][y]
代表的意义是从左边或从上边来的最小值,再走一步,加上该位置本身含有的数字。

这就是我们的递推关系式。

3.确定初始值。

初始值就是不能用递推关系计算的情况。
那么啥条件下不能用递推关系式计算呢?
当然是x=0或y=0时,此时只有可能从上方过来或者是从左边过来,不能用递推关系式来解决,
那么我们就需要计算初始值

dp[x][0]=dp[x-1][0]+num[x][0];

当坐标为(0,0)时dp[0][0]=num[0][0]

for (i = 1; i < m; i++)
	{
		dp[i][0] = dp[i - 1][0] + x[i][0];
	}
for (j = 1; j < n; j++)
	{
		dp[0][j] = dp[0][j - 1] + x[0][j];
	}

在这里插入图片描述
我们完成了图中的这一步。,蓝色部分为初始化
注:图片均来自来自Leetcode题解

leetcode

4.利用初始值与关系式求目标值

由于左边与最上边已经初始化,我们要从**坐标(1.1)**开始计算值知道(m-1,n-1)

for (i = 1; i < m; i++)
	{
		for (j = 1; j < n; j++)
		{
			dp[i][j] = Min(dp[i - 1][j], dp[i][j - 1]) + x[i][j];
		}
	}

这样我们就求出了dp[m-1][n-1].
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
求解dp[x-1][y-1]

代码展示(利用动态数组)

#include<stdio.h>
#include<stdlib.h>
void Free(int** x, int col);//释放动态数组
int Min(int x, int y);//求最小值
int minway(int** x, int m, int n);//求最小路径的和
int main()
{
	int m, n;
	scanf("%d %d", &m, &n);
	int i, j;
	int** x;
	x = (int**)malloc(sizeof(int*) * m);
	for (i = 0; i < m; ++i)
	{
		x[i] = (int*)malloc(sizeof(int) * n);
	}
	for (i = 0; i < m; i++)
	{
		for (j = 0; j < n; j++)
			scanf("%d\n", &x[i][j]);
	}//动态二维数组的基本操作,建立与输出。
	int num;
	num = minway(x, m, n);
	printf("%d\n", num);
	Free(x, n);释放内存
	return 0;
}
int minway(int** x, int m, int n)
{
	int i;
	int** dp;
	dp = (int**)malloc(sizeof(int*) * m);
	for (i = 0; i < m; ++i)
	{
		dp[i] = (int*)malloc(sizeof(int) * n);
	}//二维数组的建立。
	i = 0;
	int j = 0;
	dp[0][0] = x[0][0];//初始化
	//初始化最左边
	for (i = 1; i < m; i++)
	{
		dp[i][0] = dp[i - 1][0] + x[i][0];
	}
	//初始化最上边
	for (j = 1; j < n; j++)
	{
		dp[0][j] = dp[0][j - 1] + x[0][j];
	}
	//利用初始化与递推关系式求解
	for (i = 1; i < m; i++)
	{
		for (j = 1; j < n; j++)
		{
			dp[i][j] = Min(dp[i - 1][j], dp[i][j - 1]) + x[i][j];
		}
	}
	return dp[m-1][n-1];
}
int Min(int x, int y)
{
	return x < y ? x : y;
}
void Free(int** x, int col)
{
	int i = 0;
	for (i = 0; i < col; i++)
	{
		free(x[i]);
	}
	free(x);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值