动态规划
文章目录
一、背包问题
1、01背包问题
01背包问题就是通过遍历选择是否选第i个物品,以达到总价值最大:
#include<iostream>
using namespace std;
const int N = 1010;
int n,m;//物品总数为n个,背包的总容量为m
int dp[N];
int v[N],w[N];//物品的体积和价值
int main()
{
cin >> n >> m;
for(int i = 0;i < n;i++)cin >> v[i] >> w[i];
for(int i = 0;i < n;i++)
for(int j = m;j >= v[i];j--)
dp[j] = max(dp[j],dp[j - v[i]] + w[i]);//从不选和选中选中较大值
cout << dp[m] << endl;
return 0;
}
当遍历方向为从大到小时,每次更新时使用的是上一次的状态。更新的只与i-1时的状态有关,而逆序可以保证i-1的状态为上一次循环的状态,没有被更新。
2、完全背包问题
首先看结论,完全背包问题的代码就是将01背包中的从大到小遍历改成从小到大遍历
for(int i = 0;i < n;i++)
for(int j = v[i];j <= m;j++)
dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
因为每个物品可以无限选择,所以选择的结果是多样的,此时:
dp[j] = max(dp[j],dp[j - v[i]] + w[i],dp[j - 2 * v[i]] + 2 * w[i]……)
同时,dp[j - v[i]] = max(dp[j - v[i]],dp[j - 2 * v[i]] + w[i]……)
所以,dp[j] = max(dp[j],dp[j - v[i]] + w[i])
由于这里的状态是需要及时更新的,所以这里的循环方式是正序。
3、多重背包
多重背包本质上还是利用01背包的思想处理的,每个物品可以选择多件,但是有数量限制,不同于完全背包的无限选择。
3.1、多重背包的朴素实现
for(int i = 0;i < n;i++)
{
scnaf("%d%d%d",&v,&w,&s);//输入体积价值和数量
for(int j = m;j >= v;j--)
for(int p = 1;p <= s && j >= p * v;p++)
f[j] = max(f[j],f[j - p * v] + p * w);
}
3.2、二进制优化多重背包
二进制优化多重背包是指利用2的次方将第i物品拆分为多个物品,并单独作为一个物品利用01背包计算结果。
比如,8拆分为1,2,4,1;10拆分为1,2,4,3。即20,21,22,23……2n,余数。
struct Good
{
int v,w;
};
vector<Good>good;
void dp()
{
for(int i = 0;i < n;i++)
{
int v,w,s;
cin >> v >> w >> s;
for(int j = 1;j <= s;s -= j,j *= 2)
good.push_back({j * v,j * w});
if(s)good.push_back({s * v,s * w});
}
for(auto g : good)
{
for(int i = m;i >= g.v;i--)
f[i] = max(f[i],f[i - g.v] + g.w);
}
}
4、分组背包
分组背包级有讲物品分为多组,每组有多个物品,但是每组物品最多只能选择一个。
for(int i = 0;i < n;i++)
{
int s;
cin >> s;
for(int j = 0;j < s;j++)cin >> v[j] >> w[j];
for(int j = m;j > 0;j--)
{
for(int p = 0;p < s;p++)
{
f[j] = max(f[j],f[j- v[p]] + w[p]);
}
}
}
二、线性DP
1、数字三角形
存在以下数字三角形,求和最大路径的值。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从最后一层开始dp即可,每次用较大的值加上上一个值,即可得到结果:
for(int i = n - 1;i > 0;i--)
{
fo(int j = 1;j <= i;j++)
{
num[i][j] += max(num[i][j],num[i][j + 1]);
}
}
最后的结果即为3num[1] [1]的值。
2、最长上升子序列
2.1、最长上升子序列的朴素实现
最长上升子序列的求法就是在每个点的位置存入以该点为结尾的最长子序列的长度,然后遍历是实现即可。
for(int i = 1;i <= n;i++)
for(int j = 1;j < i;j++)
f[i] = max(f[i],f[j] + 1);
2.2、优化
在储存子序列末尾数字的时候,取最小的存储