挂饰——题解思路

序言:

在一个中午我们和初二的同学一起做题,我把第一题做起了之后。竟开始了划水,想都没怎么想挂饰这道题,所以今天趁着闲的没事干时间充裕来复习一下,总结一下。


挂饰 洛谷P4138

思路:

简而言之,这是一道01背包——一道比较好的题。
费用是挂钩(二维)亦可以一维只是本人能力有限

注意!状态转移方程一定不能写成这个样子:

f[i][j]=max(f[i-1][j],f[i-1][j-w[i]+1]+v[i]);

为什么?!

因为因为j-w[i]+1可能是个负数,没有意义,这时候就要考虑这物品直接挂在手机上即j=1,也就需要我们把j-w[i]和0取最大值保证有意义。

所以正确的状态转移方程应该是这个鸭子:

f[i][j]=max(f[i-1][j],f[i-1][max(j-w[i].a,0)+1]+w[i].b);

这里的a是钩子,b是价值。

然后赋初值
我们应该把不可能的情况赋成极小值即

 f[0][i]=MAXN,f[i][n+1]=MAXN;

然后f[0][1]=0这是初始状态。


那么为什么要排序呢?

我猜各位大佬应该都明白,但是我还是要讲讲:
因为要让喜悦度最大的那些物品先挂上,所以要排序。

注意:

如何处理数组越界的情况:

1≤N≤2000 ,0≤Ai≤N(1≤i≤N)
可以看到挂钩最多可以有大约2000 × 2000 = 4000000 个,显然我们不能开这么多数组来一个一个转移 , 要不然就 T 了。
通过观察我们看到 N 只有2000 ,也就是最多只会使用 2000 个钩子,所以当计算到钩子数超过2000的转移,我们直接把结果的钩子数量当做2000来看,因为2000就是极限了,这并不会影响答案。

为了应对负数的情况,我让 maxx [ ] 的下标全部增加 2000 ,这样就可以表示在负数范围内的值了。


上AC代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = -1000000000;
int f[4005][4005];
struct leimu {
    
    
    int a, b;
} w[4005];
bool cmp(leimu i, leimu j) {
    
     return i.a > j.a; }
int ans = -2147483647;
int main() {
    
    
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d%d", &w[i].a, &w[i].b);
    sort(w + 1, w + 1 + n, cmp);
    for (int i = 0; i <= n; i++) f[0][i] = MAXN, f[i][n + 1] = MAXN;
    f[0][1] = 0;
    for (int i = 1; i <= n; i++) {
    
    
        for (int j = 0; j <= n; j++) {
    
    
            f[i][j] = max(f[i - 1][j], f[i - 1][max(j - w[i].a, 0) + 1] +
                                           w[i].b);  // f[i][j]表示前i件物品在有j个挂钩的情况下的最大价值
        }
    }
    for (int i = 0; i <= n; i++) ans = max(ans, f[n][i]);  //每个钩子都有可能
    printf("%d", ans);
    return 0;
}

我开结构体的原因也是借鉴了其他大佬的方法,更省事:我们要进行一个排序让钩子多的在前面先计算,这个因为把钩子少的先计算很有可能多次挂在手机上没有意义。

结束


小结:

动态规划的每一道题都值得我们去分析,去理解每一个状态转移方程的意义,才能够更加大佬厉害。

猜你喜欢

转载自blog.csdn.net/C202207LYX/article/details/106892379