洛谷 P5662 [CSP-J2019] 纪念品 题解

题目链接

洛谷 P5662

题目大意

已知 \(N\) 个商品未来 \(T\) 天的价格,现在手上有 \(M\) 元钱,求 \(T\) 天后通过买卖商品最多能获得多少钱。

思路分析

首先,持有一个纪念品等同于每天买下这个纪念品,第二天卖出后再迅速买入,第三天卖出后再迅速买入……

证明过程 假设这个纪念品第 $i$ 天的价格为 $p_i$,则在第 $1$ 天至第 $n$ 天持有这一纪念品能赚 $p_n-p_1$ 元,而在每一天卖出后又迅速买入能赚 $$ \begin{aligned} \sum_{i=2}^{n}(p_i-p_{i-1})&=p_2-p_1+p_3-p_2+\cdots+p_n-p_{n-1} \\ &=p_n-p_1 \end{aligned} $$ 二者相等,所以可以替代

所以我们可以从第 \(1\) 天遍历至第 \(T-1\) 天,每天对 \(N\) 个商品进行一次完全背包,对钱数 \(M\) 进行更新。

考虑动态规划。设 \(dp_i\) 表示第 \(i\) 天可以赚到的钱数,我们以物品当日的价格 \(p_{i,j}\) 作为重量,赚到的钱 \(p_{i+1,j}-p_{i,j}\) 为价值,所以状态转移方程为

\[dp_i=\max(dp_i,dp_{i-p_{i,j}}+p_{i+1,j}-p_{i,j}) \]

代码

注意 dp 时第 \(t\) 天无法买入了,所以外层循环 \(i\) 时只要循环至 \(T-1\) 即可。同时注意 \(m\) 在交易后会发生变化,需要及时更新。

#include<bits/stdc++.h>
using namespace std;

const int N=105,M=1e4+10;
int t,n,m;
int p[N][N],dp[M];

int main(){
    scanf("%d%d%d",&t,&n,&m);
    for (int i=1;i<=t;++i){
        for (int j=1;j<=n;++j) scanf("%d",&p[i][j]);
    }
    for (int i=1;i<t;++i){
        memset(dp,0,sizeof dp);
        for (int j=1;j<=n;++j){
            for (int k=p[i][j];k<=m;++k) dp[k]=max(dp[k],dp[k-p[i][j]]+p[i+1][j]-p[i][j]);
            //完全背包模板
        }
        m+=dp[m];//因为获得的钱可以再用于交易,所以每天要更新 m
    }
    printf("%d",m);
    return 0;
}
posted @ 2025-10-17 21:28  CodingJuRuo  阅读(11)  评论(0)    收藏  举报