这里先给出朴素做法,但是会TLE。因为这里时间复杂度最坏是N的三次方,也就是1e9比较慢,下面再给出优化的代码,
#include
#include
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main ()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++ )
scanf("%d%d", &v[i], &w[i]);
for(int i = 1; i <= n; i ++ )
for(int j = 0; j <= m; j ++ )
for(int k = 0; k * v[i] <= j; k ++ )
f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
cout<<f[n][m]<<endl;
return 0;
}
这是对完全背包问题的朴素解法的优化版本,主要是通过状态转移方程的优化来提高效率。
朴素版本中的状态转移方程为:
f[i][j]=max(f[i−1][j],max0≤k≤j/v[i]{f[i−1][j−k⋅v[i]]+k⋅w[i]})
在优化版本中,我们对状态转移方程进行改写,通过观察可知:
max0≤k≤j/v[i]{f[i−1][j−k⋅v[i]]+k⋅w[i]}
可以分解为两部分:
max0≤k≤j/v[i]{f[i−1][j−k⋅v[i]]}: 这部分与 jj 无关,只与 ii 和 v[i]v[i] 有关;
max0≤k≤j/v[i]{k⋅w[i]}max0≤k≤j/v[i]{k⋅w[i]}: 这部分与 jj 直接相关,与 ii 和 v[i]v[i] 无关。
因此,我们可以将上述两部分分开考虑。首先,将第一部分提到循环外:
temp=max0≤k≤j/v[i]{f[i−1][j−k⋅v[i]]}
然后在内层循环中直接计算第二部分:
f[i][j]=max(f[i−1][j],temp+k⋅w[i])
这样,通过优化,将原来的二重循环变为了单重循环,提高了效率。最终的状态转移方程为:
f[i][j]=max(f[i−1][j],f[i][j−v[i]]+w[i])f[i][j]=max(f[i−1][j],f[i][j−v[i]]+w[i])
这样的优化在完全背包问题中非常常见,能够有效提高程序运行速度。
#include
#include
using namespace std;
const int N = 1010;
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 = 0; j <= m; j ++ )
{
f[i][j] = f[i - 1][j];
if(j >= v[i])
f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]);
}
cout<<f[n][m]<<endl;
return 0;
}