P4138 [JOISC2014] 挂饰

· · 题解

题解区中没有刷表法的做法,也就是从第 i 阶段转移到第 i+1 阶段。自我感觉这种方法更方便,且不易出错。

可以把 a_i 看作容量,把 b_i 看作价值,这道题就变成了背包容量可以扩张的问题。
不过直接做背包会出现一些问题:

接下来就是 DP 的转移方程:
dp_{i,j} 为考虑到第 i 个挂件,当前空闲挂钩数量为 j 时的最大喜悦值。
考虑刷表法:

dp_{i+1,k}=\max(dp_{i+1,k},dp_{i,k},dp_{i,j}+b_{i+1})

其中:

k=j+a_{i+1}-1

也就是分为两种情况,第 i+1 个挂件选还是不选。
j+a_{i+1}-1>N 时,就将其认为是 N

代码如下:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define pll pair<ll,ll>
using namespace std;
const int N=2015;
const long long INF=0x3f3f3f3f3f3f3f3fll;

inline void FileIO() {
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
}

template<typename Type>
inline void read(Type& res) {
    Type x=0,f=1;
    char c=' ';
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    res=x*f;
}

#define x first
#define y second

pii a[N];
ll dp[N][N],ans;
int n;

signed main() {
//  不开longlong见祖宗!
//  检查数组大小!
//  禁止在int乘int时不开longlong!
//  注意输出格式! 
//  STL数据类型拷贝赋值时间复杂度是 O(N)!
//  读入单个字符要用字符串,防毒瘤数据!
//  (1<<63)爆ll,(1<<31)爆int!
//  比赛结束前5min,检查上述7项,并编译运行!!!!! 
//  -std=c++14 -O2 -Wall -Wextra -Wshadow
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i].x);
        read(a[i].y);
    }
    sort(a+1,a+n+1);
    reverse(a+1,a+n+1);
    memset(dp,-0x3f,sizeof(dp));
    dp[0][1]=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<=n;j++){
            int k=min(n,j+a[i+1].x-1);
            dp[i+1][k]=max(dp[i+1][k],max(dp[i][j]+a[i+1].y,dp[i][k]));
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=n;j++){
            ans=max(ans,dp[i][j]);
        }
    }
    printf("%lld\n",ans);
    return 0;
}