P5662纪念品 题解
前言
第一次发题解,多有瑕疵。
阅读题解之前务必熟悉题目
解题思路
考试刚看到题的时候,直觉便是动态规划,可硬是推了半天没推出来。(怪我太蒻了)
不过没关系,先把能写的写了…
int p[105][105];//存储每天每种纪念品的价格
int liRun[105];//存储每两天之间的利润(两天的价格差)
每件商品每天都有不一样的价格,而且每天可以进行无限次买卖,这很容易让人想到计算两天之间纪念品的差价(可获利润)。(like this)
int t,n,m;cin>>t>>n>>m;//t天,n种纪念品,初始金币m个
for (int i = 1;i <= t;i++)
for (int j = 1;j <= n;j++)
cin>>p[i][j];//读入每天的价格
for (int i = 1;i <= t-1;i++)
for (int j = 1;j <= n;j++)
liRun[i][j] = p[i+1][j] - p[i][j];//单件纪念品的利润
看出来了啥? 背包呀!
为什么可以这么分析呢?
每种纪念品我们都可以转换为利润之间的关系,我们就可以把金币的总数看作是背包的大小,每种纪念品当天的价值看作重量,利润就是每种纪念品的价值。
我在思考的时候就提出了这样的疑问——假设一种商品要第一天买,第三天卖才可以获得最大利润,但是在第二天卖出又是第二天的最优解,该怎么办呢?
我们就不要考虑比较第利益的大小,因为我们是利用的是两天之间的这个过程,所以可以理解为第一天买进,第二天卖出再买进(这一步的实现来源于题目中给出每天每种物品可以买进卖出无限次,所以买来再卖掉,就可以理解为跳过了第二天,从而实现了最优解),然后再第三天再卖出。
所以这道题其实就是一个完全背包,当我们把纪念品当天的价格当作背包中物品的重量,把利润当作背包中的价值,如果我们用 f[v] 表示空间为v时的物品最大价值,那么我们可以得到:
f[v] = max(f[v],f[v - p[i][j] + liRun[i][j])
另一个需要注意的点是,背包的大小(手里的金币)是在不断增加的,所以每次求背包的最优解的时候,一定要排除价值为负数的纪念品(负利润),并且每隔一天刷新一下背包的大小(手里的金币),然后就可以得到我们的主程序了。
for (int i = 1;i <= t-1;i++){//天数,利润的计算介于两天之间所以只用t-1天就好
for (int j = 1;j <= n;j++)//纪念品种数
if (liRun[i][j] > 0)//排除所有的负价值(负利润)
for (int v = p[i][j];v <= m;v++)
//体积直接从 p[i][j]开始,反正之前的也放不下 ,一直遍历到当前手里的金币数量
f[v] = max(f[v],f[v-p[i][j]] + liRun[i][j]);//完全背包求解
m += f[m];//更新手里的金币
memset(f,0,sizeof(f));//数组清0,开始第二轮背包
}
然后就AC了
最后给大家看一下完整的代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int f[10005];// 背包
int p[105][105];//每天每种的价位
int liRun[105][105];//每两天之间的利润
int main(){
int t,n,m;cin>>t>>n>>m;//t天,n种纪念品,初始金币m个
for (int i = 1;i <= t;i++)
for (int j = 1;j <= n;j++)
cin>>p[i][j];//读入每天的价格
for (int i = 1;i <= t-1;i++)//天润的计算介于两天之间所以只用t-1天就好
for (int j = 1;j <= n;j++)
liRun[i][j] = p[i+1][j] - p[i][j];//单件纪念品的利润
for (int i = 1;i <= t-1;i++){//天数,利润的计算介于两天之间所以只用t-1天就好
for (int j = 1;j <= n;j++)//纪念品种数
if (liRun[i][j] > 0)//排除所有的负价值(负利润)
for (int v = p[i][j];v <= m;v++)
//体积直接从 p[i][j]开始,反正之前的也放不下 ,一直遍历到当前手里的金币数量
f[v] = max(f[v],f[v-p[i][j]] + liRun[i][j]);//完全背包求解
m += f[m];//更新手里的金币
memset(f,0,sizeof(f));//数组清0,开始第二轮背包
}
cout<<m;//输出
return 0;
}