动态规划 - 打家劫舍机器人路径0-1背包
Posted Jqivin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划 - 打家劫舍机器人路径0-1背包相关的知识,希望对你有一定的参考价值。
一、动态规划
关于动态规划算法,和分治策略类似。都是将问题分解然后求解,但是又和分治策略有所不同,动态规划把每次求得的解都记录在了一张表中,省去了重复递归的过程。
二、例子
1. 打家劫舍
(1)题目描述
(2)解题思路
(3)代码展示
//使用vector表示dp
int rob(const vector<int>& num)
{
int n = num.size(); //房子的个数
vector<int> dp(n);
if (n == 1)
{
return num[0];
}
if (n == 2)
{
return std::max(num[0], num[1]);
}
dp[0] = num[0];
dp[1] = std::max(num[0], num[1]);
for (int i = 2; i < n; i++) //n为个数,i到n-1退出
{
dp[i] = std::max(dp[i - 1], dp[i - 2] + num[i]); //不能是连续的两家
}
return dp[n - 1]; //第n-1个是最优解
}
int main()
{
vector<int> num1 = { 1,2,3,1 };
vector<int> num2 = { 2,7,9,3,1 };
cout << rob(num1) << endl;
cout << rob(num2) << endl;
return 0;
}
结果:
//不使用vector表示dp
//把原来的dp换成三个变量就可以了
int rob1(const vector<int>& num)
{
int n = num.size();
if (n == 1)
{
return num[0];
}
if (n == 2)
{
return std::max(num[0], num[1]);
}
int pre = 0; //当前的前一个
int cur = num[0]; //当前
int tmp = 0;
for (int i = 1; i < n; i++)
{
tmp = std::max(cur, pre + num[i]);
pre = cur;
cur = tmp;
}
return tmp;
}
2.机器人路径
(1)题目描述
(2)解题思路
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
(3)代码展示:
int uniquePaths(int m, int n)
{
if (m <= 0 || n <= 0)
return 1;
vector<vector<int>> dp(m, vector<int>(n, 0));
int i, j;
for (i = 0; i < n; i++)
{
dp[0][i] = 1;
}
for (j = 0; j < m; j++)
{
dp[j][0] = 1;
}
for (i = 1; i < m; i++) //注意越界,i == 1, 不要写成i = 0了,下面有i-1,j-1
{
for (j = 1; j < n; j++) //注意越界,j == 1
{
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
3. 0-1背包问题
题目描述
给定n种物品和一背包。物品i的重量是wi;,其价值为vi, ,背包的容量为C。问:应该如何选择装人背包的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装人背包。不能将物品i装入背包多次,也不能只装入部分的物品i。因此,该问题称为0-1背包问题。
解题思路
首先定义一些变量:Vi表示第 i 个物品的价值,Wi表示第 i 个物品的体积,定义V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的价值,同时背包问题抽象化(X1,X2,…,Xn,其中 Xi 取0或1,表示第 i 个物品选或不选)。V(i,j)为什么要用两个值来表示,因为价值与两个属性有关。它与空间和元素都有关系。
1、建立模型,即求max(V1X1+V2X2+…+VnXn);
2、寻找约束条件,W1X1+W2X2+…+WnXn<capacity;
3、寻找递推关系式,面对当前商品有两种可能性:①容量不够 ②容量足够
-
①包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即V(i,j)=V(i-1,j);
-
②还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}。V(i-1,j),V(i-1,j-w(i))的值通过读表可以获得。
注意:价值是由i和j共同决定的。
由此可以得出递推关系式:
- j<w(i) V(i,j)=V(i-1,j)
- j>=w(i) V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}
4、填表,首先初始化边界条件,V(0,j)=V(i,0)=0;然后执行递推关系式。
(1)递归求解:通过逐次将规模减小,来递归的求解最大的值。
①正序: 从前向后规模递增。i表示从1到i的范围内。
②逆序: 从后向前规模递增。i表示从i到n。
代码展示
//递归求解-正序
int Knapsack(int W[], int V[], int i, int j)
{
if (i == 1) //只有一个物品,第一个
{
return j > W[i] ? V[i] : 0;
}
else if (W[i] > j) //剩余空间过小,放不下第i个
{
return Knapsack(W, V, i - 1,j);
}
else //能放下,进行比较,取最优
{
int max1 = Knapsack(W, V, i - 1, j);
int max2 = Knapsack(W, V, i - 1, j - W[i]) + V[i];
return max1 > max2 ? max1 : max2;
}
}
//递归求解-逆序
int Reverse_knapsack(int W[], int V[], int i, int n, int j)
{
if (i == n) //只有一个物品,最后一个
{
return j >= W[n] ? V[n] : 0;
}
else if (W[i] > j) //剩余空间过小,放不下第i个
{
return Reverse_knapsack(W, V, i + 1, n, j);
}
else //能放下,进行比较,取最优
{
int max1 = Reverse_knapsack(W, V, i + 1, n, j);
int max2 = Reverse_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 max1 = Knapsack(W, V, n, c);
cout << "max1:" << max1 << endl;
int max2 = Reverse_knapsack(W, V, 1,n,c);
cout << "max2:" << max2 << endl;
return 0;
}
结果:
(2)非递归求解
解题思路
用表格来记录值,用的时候直接查表格。从小到大开始记录。
代码展示
void BackPack(vector<vector<int>>& dp, int W[], int n, int c, vector<bool>& X)
{
int j = c;
for (int i = n; i > 0; i--)
{
if (dp[i][j] != dp[i - 1][j])
{
X[i] = true;
j -= W[i];
}
}
}
void Print_Vector(vector<vector<int>>& ar, int m, int n)
{
for (int i = 0; i <= m; i++)
{
for (int j = 0; j <= n; j++)
{
printf("%4d", ar[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 (W[1] < j)
{
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];
}
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 };
vector<vector<int> > dp(n+1,vector<int>(c+1,0));
vector <bool> X(n + 1, false);
int max = Knapsack(W, V, n, c,dp);
cout << max << endl;
Print_Vector(dp,n,c);
BackPack(dp, W, n, c, X);
int count = 0;
for (auto x : X)
{
if (x)
{
cout << "第" << count << "个放进去了!价值是" << V[count] << endl;
}
count++;
}
return 0;
}
结果:
以上是关于动态规划 - 打家劫舍机器人路径0-1背包的主要内容,如果未能解决你的问题,请参考以下文章