6.8 KiB
AcWing 1163. 纪念品
一、题目描述
小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。
某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。
每天,小伟可以进行以下两种交易无限次:
任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品,注意 同一个纪念品可以在同一天重复买;
卖出持有的任意一个纪念品,以当日价格换回金币。 每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。
当然,一直持有纪念品也是可以的。
T 天之后,小伟的超能力消失。
因此他一定会在第 T 天卖出所有纪念品换回金币。
小伟现在有 M 枚金币,他想要在超能力消失后拥有尽可能多的金币。
输入格式 第一行包含三个正整数 $T,N,M$,相邻两数之间以一个空格分开,分别代表未来天数 $T$,纪念品数量 $N$,小伟现在拥有的金币数量 $M$。
接下来 T 行,每行包含 N 个正整数,相邻两数之间以一个空格分隔。第 i 行的 N 个正整数分别为 $P_{i,1},P_{i,2},……,P_{i,N}$,其中 P_{i,j} 表示第 i 天第 j 种纪念品的价格。
输出格式 输出仅一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。
数据范围
对于 10\% 的数据,$T=1$。
对于 30\% 的数据,$T≤4,N≤4,M≤100$,所有价格 $10≤P_{i,j}≤100$。
对于 15\% 的数据,$T≤100,N=1$。
对于 15\% 的数据,$T=2,N≤100$。
对于 100\% 的数据,$T≤100,N≤100,M≤10^3$,所有价格 $1≤P_{i,j}≤10^4$,数据保证任意时刻,小明手上的金币数不可能超过 $10^4$。
输入样例1:
6 1 100
50
20
25
20
25
50
输出样例1:
305
输入样例2:
3 3 100
10 20 15
15 17 13
15 25 16
输出样例2:
217
样例解释 样例#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 枚金币。
二、算法解析
(贪心,$DP$) $O(TNM)$
关键点
在某一种方案中,如果我们在第$i$天买入第$j$天卖出,其中 $i<j$,则等价于在第 $i$天买入,第
i+1天卖出,第i+1天再买入, …, 在第j天卖出。
解读:比如纪念品
A,我们假设最优策略是第$1$天买入,第$7$天卖出是最优的,可以选择的等价策略是第$1$天买入,第$2$天卖出,第$2$天再买入,第$3$天卖出,...,第$7$天卖出,这与我们的原始策略是一样的。 按数学思路来说明一下,第$1$天买入,第$7$天卖出=P_7-P_1第$1$天买入,第$2$天卖出,第$2$天买入,第$3$天卖出,...第$7$天卖出=-P_1+P_2-P_2+P_3-P_3+P_4-P_4+P_5-P_5+P_6-P_6+P_7=P_7-P_1两者是一样的。
这样的话,所有交易不会跨两天,因此目标就变成了 贪心策略:先尽可能使第二天的钱最多,再尽可能使第三天的钱最多,依次类推直到最后一天。
解读:这个为什么是正确的呢? 可以用反证法:如果我第$2$天亏一些,有没有可能让第$3$天更赚呢? 这是不可能的,比如第$2$天我剩下的钱是可能是$m_1$个金币,也可能是$m_2$个金币,而且满足
m_1<m_2,那么$m_2$一定比$m_1$要好,这是显然的,为什么呢? 因为我在购买下一天的纪念品的时候,肯定是手中的钱越多越多,因为如果钱少,那么可能有些纪念品我就买不起,就会少一些机会,结果不可能变得更好。$m_1$块钱可以购买的所有纪念品,$m_2$块钱肯定是可以购买到的,反过来就不是了。$m_2$考虑的所有方案,一定包含$m_1$考虑的所有方案,所以$m_2$考虑所有方案的最大值一定比$m_1$考虑的所有方案的最大值要大。所以,得到结论:我们每天都要争取最大值,这样,最终就是最大值。注:类似于邻项交换的贪心策略
接下来的问题变成:
假设第
i天的钱数是 $m_i$,那么第i+1天的钱数最多是多少呢?
这是一个经典的 完全背包 问题:
- 背包容量是 $m_i$;
- 每个股票都是一种物品,体积是第
i天买入的价格,价值在第i天买入第i+1天卖出的收益; 每天可以进行无限次交易,因此每种物品都是可以用无限次; 那么第i+1天的最大收益m_{i+1}就是m_i加上背包模型所求出的最大收益。
本题时间复杂度恰好是 $10^8$,比较极限,因此可以加一些优化:
- 只有当某支股票的收益为正数时,才考虑使用该物品。
时间复杂度
一共有 T≤100 天,每天有 N≤100 支股票,每天的钱数最大是 $M≤10000$。
算法中一共会算 T−1 次背包模型,每算一次的时间复杂度是 $O(NM)$,因此总时间复杂度是 $O(TNM)=10^8$。
三、实现代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 10010;
int t, n, m;
int f[M];
int w[N][N];
int main() {
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> t >> n >> m;
for (int i = 1; i <= t; i++)
for (int j = 1; j <= n; j++)
cin >> w[i][j];
for (int i = 1; i < t; i++) { // t-1轮背包
memset(f, 0, sizeof f);
for (int j = 1; j <= n; j++)
if (w[i + 1][j] > w[i][j]) // 优化:只有在价值为正数时才会考虑使用该物品
for (int k = w[i][j]; k <= m; k++)
f[k] = max(f[k], f[k - w[i][j]] + w[i + 1][j] - w[i][j]);
m += f[m];
}
printf("%d\n", m);
return 0;
}