动态规划

动态规划问题

动态规划法的基本思想

  • 动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题, 先求解子问题,然后从这些子问题的解得到原问题的解。
  • 与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是互相独立的。
  • 若用分治法解这类问题,则分解得到的子问题数目太多,以至于最后解决原问题需要耗费指数时间。然而,不同子问题的数目常常只有多项式量级。在用分治法求解时,有些子问题被重复计算了许多次。如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算,从而得到多项式时间算法。
  • 为了达到这个目的,可以用一个表来记录所有已解决的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填人表中。

具体的动态规划算法是多种多样的,但它们具有相同的填表格式。

动态规划算法适用于解最优化问题。

通常可以按以下步骤设计动态规划算法:

(1)找出最优解的性质,并刻画其结构特征;

(2)递归地定义最优值;

(3)以自底向上的方式计算出最优值;

(4)根据计算最优值时得到的信息,构造最优解。

  • 步骤(1)~(3)是动态规划算法的基本步骤。
  • 在只需要求出最优值的情形,步骤(4)可以省去。
  • 若需要求问题的最优解,则必须执行步骤(4)。
  • 此时,在步骤(3)中计算最优值时,通常需记录更多的信息,以便在步骤(4)中,根据所记录的信息,快速构造出最优解。

下面以具体例子说明如何运用动态规划算法的设计思想,并分析可用动态规划算法求解的问题应该具备的一般特征。

例题一 、打家劫舍:问题描述
代码示例

1.找出能偷到的最高金额,并用表记录偷窃过程。

int rob(const vector<int>& nums, vector<bool>& X)
{
	if (nums.empty()) return 0;
	vector<int> dp(nums.size());
	if (nums.size() == 1)
	{
		X[0] = 1;
		return nums[0];
	}
	if (nums.size() == 2)
	{
		if (nums[0] > nums[1])
		{
			X[0] = 1;
			return nums[0];
		}
		else
		{
			X[1] = 1;
			return nums[1];
		}
	}
	dp[0] = nums[0];
	if (nums[0] > nums[1])
	{
		X[1] = 1;
		dp[1] = nums[0];
	}
	else
	{
		X[1] = 1;
		dp[1] = nums[1];
	}
	int len = nums.size();
	for (int i = 2; i < len; ++i)
	{
		dp[i] = std::max(dp[i - 1], dp[i - 2] + nums[i]);
		// X[i]  待补充
	}
	return dp[len - 1];
}
int main()
{
	vector<int> ar = { 2,7,9,3,1 };
	vector<bool> X(ar.size());
	cout << rob(ar, X) << endl;
	return 0;
}

2.找出能偷到的最高金额,无记录过程。

int rob(const vector<int>& nums)
{
	if (nums.empty()) return 0;
	if (nums.size() == 1)
	{
		return nums[0];
	}
	int pre = 0;         //dp[i-2];
	int cur = nums[0];	 //dp[i-1];
	int len = nums.size();
	for (int i = 1; i < len; ++i)
	{
		int tmp  = std::max(cur, pre + nums[i]);
		pre = cur;
		cur = tmp;
	}
	return cur;
}

int main()
{
	vector<int> ar = { 2,7,9,3,1 };
	cout << rob(ar) << endl;
	return 0;
}

例题二、机器人路径
在这里插入图片描述

int GetPaths(int m, int n)
{
	if (m <= 1 || n <= 1) return 1;
	vector<vector<int> > dp(m, vector<int>(n, 1));//处理只有一行一列的情况
	for (int i = 1; i < m; ++i)
	{
		for (int j = 1; j < n; ++j)
		{
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
		}
	}
	return dp[m - 1][n - 1];
}
int main()
{
	int m, n;
	cin >> m >> n;
	cout << GetPaths(m, n) << endl;
}

例题三、0-1背包问题
在这里插入图片描述
代码示例:

1.不记录放置物品的情况

int Knapsack(int W[], int V[], int i, int j,vector<vector<int> >& dp)//反向计算
{
	if (i == 1)
	{
		dp[i][j] = j >= W[1] ? V[1] : 0;
	}
	else if (dp[i][j] > 0)
	{
		return dp[i][j];
	}
	else if (j < W[i])
	{
		dp[i][j] = Knapsack(W, V, i - 1, j, dp);
	}
	else
	{
		int max1 = Knapsack(W, V, i - 1, j, dp);
		int max2 = Knapsack(W, V, i - 1, j - W[i], dp) + V[i];
		dp[i][j] = max1 > max2 ? max1 : max2;
	}
	return dp[i][j];
}

int Knapsack(int W[], int V[], int i, int n, int j)//正向计算
{
	if (i == n)
	{
		return j >= W[n] ? V[n] : 0;
	}
	else if(j< W[i] )
	{
		return Knapsack(W, V, i + 1, n, j);
	}
	else
	{
		int max1 = Knapsack(W, V, i + 1, n, j);
		int max2 = Knapsack(W, V, i + 1, n, j - W[i]) + V[i];
		return max1 > max2 ? max1 : max2;
	}
}
int main()
{
	const int n = 5;
	const int c = 10;
	int W[n + 1] = { 0,2,2,6,5,4 };
	int V[n + 1] = { 0,6,3,5,4,6 };
	int maxv = Knapsack(W, V,1, n, c);

	cout << maxv << endl;
}

2.记录放置物品的情况

void Print_Vector(vector<vector<int> >& c, int m, int n)
{
	for (int i = 0; i <= m; ++i)
	{
		for (int j = 0; j <= n; ++j)
		{
			printf("%4d", c[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

int Knapsack(int W[], int V[], int n, int c,vector<vector<int> > &dp)
{
	for (int j = 1; j <= c; ++j)
	{
		if (j >= W[1])
		{
			dp[1][j] = V[1];
		}
		else
		{
			dp[1][j] = 0;
		}
	}
	for (int i = 2; i <= n; ++i)
	{
		for (int j = 1; j <= c; ++j)
		{
			if (j < W[i])
			{
				dp[i][j] = dp[i - 1][j];
			}
			else
			{
				dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - W[i]] + V[i]);
			}
		}
	}

	return dp[n][c];
}
void BackPack(int W[], const vector<vector<int> >& dp,int n, int c, bool X[])
{

	for (int i = n; i > 0; --i)
	{
		if (dp[i][c] != dp[i - 1][c])
		{
			X[i] = true;
			c -= W[i];
		}
	}
}
int main()
{
	const int n = 5;
	const int c = 10;
	int W[n + 1] = { 0,2,2,6,5,4 };
	int V[n + 1] = { 0,6,3,5,4,6 };
	bool X[n + 1] = {};
	vector<vector<int> > dp(n + 1, vector<int>(c + 1, 0));
	int maxv = Knapsack(W, V, n, c,dp);


	cout << maxv << endl;
	Print_Vector(dp, n, c);
	BackPack(W, dp, n, c,X);
	return 0;
}

图论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值