P5662 [CSP-J2019] 纪念品

DP 题。

容易发现一个物品如果持有多天,可以转换为每拿一天,就卖掉,如果明天还持有,再买回来即可。

为了方便,我们不妨叫它:长期持有定理。

定义 ii 为购买第 ii 件物品,jj 为提供预算,kk 表示第 kk 天,ai,ja_{i,j} 代表物品 ii,第 jj 天的价格,fif_i 表示提供 ii 元预算能赚的钱,状态转移方程如下:

fj=max(fj,fjai,kai,k+ai,k+1)f_j = \max(f_j, f_{j - a_{i,k}} - a_{i,k} + a_{i,k + 1})

答案即为 fmf_m

解释一下原因。

max\max 前一项即今日不购买第 ii 件物品,后一项我们拆开看:

  • fjai,kf_{j - a_{i,k}},购买后剩的钱能买到的最大金额。
  • ai,k- a_{i,k},购买它花费的钱。
  • ai,k+1a_{i,k + 1},根据长期持有定理,我们在第二天卖出,不会影响答案。

输入时颠倒下标,使第 ii 天第 jj 种纪念品的价格变为第 jj 种第 ii 天纪念品的价格。

直接转移即可,时间复杂度 O(tnm)O(tnm)

#include<bits/stdc++.h>
using namespace std;
int t, m, n, a[105][105], f[10005];
int main(){
    cin >> t >> n >> m;
    for(int i = 1; i <= t; i ++)
        for(int j = 1; j <= n; j ++)
        cin >> a[j][i];
    for(int k = 1; k < t; k ++){//第 t 天做的事情没有用
        for(int i = 0; i <= m; i ++) f[i] = 0;//清空
        for(int i = 1; i <= n; i ++)
			for(int j = a[i][k]; j <= m; j ++)//从 1 开始下标可能为负
			f[j] = max(f[j], f[j - a[i][k]]- a[i][k] + a[i][k + 1] );//状态转移方程
        m += f[m];//赚的钱拿走
    }
    cout << m;
    return 0;
}

posted on 2024-12-22 20:57  zhangzirui66  阅读(37)  评论(0)    收藏  举报  来源

导航