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;
}