对于DP问题,首先理解y总的闫氏Dp分析法,之后在自己脑中要有一个动态填表的过程。
以一个简单的0-1背包问题为背景简单说一下自己对于维数优化和输入优化的理解:
01背包问题
源代码及相应注解:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3 + 10;//定义数据范围
int n,m;//物品数量及背包体积
int v[N],w[N];//将体积和价值存入一维数组
int f[N][N];//定义状态表示数组
int main()
{
cin>>n>>m;
for (int i = 1; i <= n; i ++ )cin>>v[i]>>w[i];
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m;j ++)
{
f[i][j] = f[i-1][j];
if(j>=v[i])f[i][j] = max(f[i-1][j],f[i-1][j-v[i]] + w[i]);
}
cout<<f[n][m]<<endl;
return 0;
}
在二维循环中,根据f[i][j]定义每一层f[i][]表示只在前i个物品中选,每一层中,根据j值变化进行状态转移,这里是取最大值。我们可以在脑海中构建一个n*m的二维表,从第一层开始填表,下面一层会用到上面一层的值。
维数优化:
在状态转移方程中,我们可以看出f[i][j] = f[i-1][j];或if(j>=v[i])f[i][j] = max(f[i-1][j],f[i-1][j-v[i]] + w[i]);每一层只会用到上一层的值,而我们最终答案也是只在最后一层取得,所以我们可以只维护一个一维数组,当层数变化时,用当前数组(未更新前保留上一层的值)根据状态转移方程更新新一层的值,但是此时就会遇到一个问题,当我们更新f[i][1]的值时(这里先用二维进行讲解),假如状态转移方程为f[i][j] = f[i - 1][j - 1] + 1;那么f[i][1] = f[i - 1][0] + 1;如果直接优化为一维f[j] = f[j - 1] + 1;f[1] = f[0] + 1;如果更新f[2]时,f[2] = f[1] + 1;但是在二维中f[i][2] = f[i - 1][1] + 1;所以更新为一维时,如果正向更新后面的j值,那么使用的就是当前层次j的值,在二维中体现为:f[i][1] = f[i][0] + 1,必出错,所以我们解决这个问题可以从后往前更新,那么这个问题就迎刃而解;
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3 + 10;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
cin>>n>>m;
for (int i = 1; i <= n; i ++)cin>>v[i]>>w[i];
for (int i = 1; i <= n; i ++)
for (int j = m; j>= v[i]; j --)
f[j] = max(f[j],f[j - v[i]] + w[i]);
cout<<f[m]<<endl;
return 0;
}
优化输入:
对于每一层的取值,我们可以通过当前层是否取当前层的物品来更新当前层的表格
继续更新完全背包正向循环的原因
持续更新中…
提问:如果有10个物品价值和体积都是1。而背包容量是1e9,这种情况下该如何优化?
hhhh,好问题,这么大容量应该不能用Dp了,毕竟打表这么大容量一定会超时。
有空看一下 7-2 拼题A打卡奖励 (25 分) 这个题哦
hhh好的