CSP-J2019纪念品

题目描述

小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。

每天,小伟可以进行以下两种交易无限次

  1. 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
  2. 卖出持有的任意一个纪念品,以当日价格换回金币。

每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。

�T 天之后,小伟的超能力消失。因此他一定会在第 �T 天卖出所有纪念品换回金币。

小伟现在有 �M 枚金币,他想要在超能力消失后拥有尽可能多的金币。

输入格式

第一行包含三个正整数 T,N,M,相邻两数之间以一个空格分开,分别代表未来天数 T,纪念品数量 N,小伟现在拥有的金币数量 M。

接下来 T 行,每行包含 N 个正整数,相邻两数之间以一个空格分隔。第 i 行的 N 个正整数分别为 ,Pi1​,,Pi2​,……PiN​,其中 Pij​ 表示第 i 天第 j 种纪念品的价格。

输出格式

输出仅一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。

输入输出样例

输入 #1                      输出 #1

6 1 100            305
50
20
25
20
25
50

输入 #2                      输出 #2

3 3 100            217
10 20 15
15 17 13
15 25 16

说明/提示

【输入输出样例 1 说明】

最佳策略是:

第二天花光所有 100 枚金币买入 5 个纪念品 1;

第三天卖出 5 个纪念品 1,获得金币 125 枚;

第四天买入 6 个纪念品 1,剩余 5 枚金币;

第六天必须卖出所有纪念品换回 300 枚金币,第四天剩余 5 枚金币,共 305 枚金币。

超能力消失后,小伟最多拥有 305 枚金币。

【输入输出样例 2 说明】

最佳策略是:

第一天花光所有金币买入 10 个纪念品 1;

第二天卖出全部纪念品 1 得到 150 枚金币并买入 8 个纪念品 2 和 1 个纪念品 3,剩余 1 枚金币;

第三天必须卖出所有纪念品换回216 枚金币,第二天剩余1枚金币,共 217 枚金币。

超能力消失后,小伟最多拥有 217 枚金币。

【数据规模与约定】

对于 10% 的数据,T=1。

对于 30% 的数据,T≤4,N≤4,M≤100,所有价格 10≤Pi,j​≤100。

另有 15% 的数据,T≤100,N=1。

另有 15% 的数据,T=2,N≤100。

对于 100% 的数据,T≤100,N≤100,M≤103,所有价格1≤Pi,j​≤104,数据保证任意时刻,小明手上的金币数不可能超过 104。

---------------------------------------------------------------------------------------------------------------------------------

那么废话不多说,进入正题!

讲解

显而易见,这是一道背包问题。具体背包中的那一种呢?因其可以交易无数次,故为完全背包。

模板题:从N件商品中选出某些不同体积不同价值的商品任意个放入容量为M的背包中,在商品容量不超过M的情况下,使得放入背包的商品价值总和最大。

核心代码如下:

for(int i=1;i<=n;i++){ 
        for(int j=w[i];j<=m;j++){
            
a[j]=max(a[j],a[j-w[i]]+c[i]);
        }
    }

注:w[i]为每件物品的重量,c[i]为价值。

那么,我们可不可以套着模板来攻克此题呢?

当然可以!

众所周知,小伟如果像赚Money,就得通过赚其中两天同种物品不同价格的差这样的途径以此谋利,也就是说,如果我们要选择买还是不买,就得判断此物但当天的价值大于还是小于另一天此物的价值。若大于,则赚了,得买;反之,则不买。

可还有一种特殊情况待考虑,如样例一,第四天买来的物品在第五天卖掉不如第六天划算,但第五天与第六天卖掉,均为盈利,这可咋整?

我们不妨推推看:

设在第四天时买来k件物品,顺着推下去,要使第五天第六天,则在第五天卖掉时,还要买回来一些物品,拿到第六天去卖掉,则盈利的钱为:原有的钱-k*20+k*25-k*25+k*50=原有的钱-k*20+k*50。达到了我们想要的效果。即比较dp[j]与dp[j-a[k][i]]+a[k+1][i]-a[k][i]。(a[k][i]为当天第i种物品的价格,k表示第k天)。

以此我们发现,每次比较相邻两天的物品价值即可有效避免以上的情况。

自此,所有问题迎刃而解,可进行总天数T-1次循环,每次循环比较相邻两天同种物品的价值,以当前总金币数为背包容量,每件物品的价格作为每种商品的体积,相邻两天同种物品的价值差作为每种商品的价值,做完全背包“模板题”。

上代码!

代码

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std;

int t,n,m,a[107][107],dp[114514],ans; //dp[i]: 有i元钱时的最大利润 

int main(){

cin>>t>>n>>m;

ans=m;

for(int i=1;i<=t;i++)   

    for(int j=1;j<=n;j++)

        cin>>a[i][j];  //第i天第j种纪念品的价值

for(int k=1;k<t;k++){       //枚举t天

    memset(dp,0,sizeof(dp));     //每次dp数组清零

    for(int i=1;i<=n;i++){       //枚举n件商品

        for(int j=a[k][i];j<=ans;j++){    //完全背包

            dp[j]=max(dp[j],dp[j-a[k][i]]+a[k+1][i]-a[k][i]);

        }

    }  

    ans+=dp[ans];    //加上今天赚的钱

}

cout<<ans<<"\n";

return 0;

}

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std;

inline int read(){

int x=0,y=1;

char ch=getchar();

while(ch<'0' ||ch>'9') {

    if(ch=='-') y=-1;

    ch=getchar();

}

while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch&15),ch=getchar();

return x*y;

}

int t,n,m,a[107][107],dp[114514],ans; //dp[i]: 有i元钱时的最大利润 

int main(){

t=read(),n=read(),m=read();

ans=m;

for(int i=1;i<=t;i++)   

    for(int j=1;j<=n;j++)

        a[i][j]=read();    //第i天第j种纪念币

for(int k=1;k<t;k++){       //枚举t天

    memset(dp,0,sizeof(dp));     //每次dp数组清零

    for(int i=1;i<=n;i++){       //枚举n件商品

        for(int j=a[k][i];j<=ans;j++){    //完全背包

            dp[j]=max(dp[j],dp[j-a[k][i]]+a[k+1][i]-a[k][i]);

        }

    }  

    ans+=dp[ans];    //加上今天赚的钱

}

cout<<ans<<"\n";

return 0;

}

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std;

int t,n,m,a[107][107],dp[114514],ans; //dp[i]: 有i元钱时的最大利润 

int main(){

ans=m;

for(int i=1;i<=t;i++)   

    for(int j=1;j<=n;j++)

        cin>>a[i][j]; //第i天第j种纪念币

for(int k=1;k<t;k++){       //枚举t天

    memset(dp,0,sizeof(dp));     //每次dp数组清零

    for(int i=1;i<=n;i++){       //枚举n件商品

        for(int j=a[k][i];j<=ans;j++){    //完全背包

            dp[j]=max(dp[j],dp[j-a[k][i]]+a[k+1][i]-a[k][i]);

        }

    }  

    ans+=dp[ans];    //加上今天赚的钱

}

cout<<ans<<"\n";

return 0;

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值