题目描述
小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。
每天,小伟可以进行以下两种交易无限次:
- 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
- 卖出持有的任意一个纪念品,以当日价格换回金币。
每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。
�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;
}