这是一道背包DP,下面我们共同看一下
第一步——状态表示:
f[i][j]:表示走到第i个物品的时候,此时天平两边之差为j,此时所获最大价值为f[i][j]的值
第二步——状态转移方程:
我们走到第i个物品的时候,我们面临着三种选择:
1.不选当前物品
2.选当前物品,将当前物品放到重量较大的那个托盘
3.选择当前物品,将当前物品放到质量较小的那个托盘
通过这三种情况,我们可以得到对应的三种状态转移方程。
即:
- f[i + 1][j] = f[i][j];
- f[i + 1][j] = f[i][j + w[i]] + w[i];
- f[i + 1][j] = f[i][abs(j - w[i])] + w[i];
我们对这三种情况去max就是f[i+1][j]的最大值了
第三步——初始化
f[0][0]=0;
memset(f, -0x3f, sizeof(f));
解释:我们的状态只能由f[0][0] = 0转移而来,其他的都不可以。
因为我们一开始的时候,天平是没有放物品的,同样也没有质量。
举个反例f[0][10] = 0;当i=0的时候,j不能等于非零数,还有,既然j大于0,也就是托盘里放了物品,价值应该是个大于0的数啊,这样很矛盾,同样,要是价值是一个大于0的数,同样不对。
第四步——边界
max{f[n][(0<=j<=m)]}
解释:我们走到第n个物品,当前天平差值小于等于m的情况中的最大的一个就是我们要求的了。
分析就到这里了,下面我们看代码
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N = 110; int n, m; int w[N]; int f[N][N * N]; int main() { cin >> n >> m; int sum = 0; for (int i = 1; i <= n; i ++ ) cin >> w[i], sum += w[i]; memset(f, -0x3f, sizeof f); f[0][0] = 0; for (int i = 1; i <= n; i ++ ) for (int j = 0; j <= sum; j ++ ) { f[i][j] = f[i - 1][j]; f[i][j] = max(f[i][j], f[i - 1][abs(j - w[i])] + w[i]); f[i][j] = max(f[i][j], f[i - 1][j + w[i]] + w[i]); } int ans = 0; for (int i = 0; i <= m; i ++ ) ans = max(ans, f[n][i]); cout << ans << endl; return 0; }