You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
6.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## [$AcWing$ $1163$. 纪念品](https://www.acwing.com/problem/content/description/1165/)
### 一、题目描述
小伟突然获得一种超能力,他知道未来 $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**
```cpp {.line-numbers}
6 1 100
50
20
25
20
25
50
```
**输出样例1**
```cpp {.line-numbers}
305
```
**输入样例2**
```cpp {.line-numbers}
3 3 100
10 20 15
15 17 13
15 25 16
```
**输出样例2**
```cpp {.line-numbers}
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$ 天卖出。
> <font color='red' size=4><b>解读</b></font>:比如纪念品$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$
> 两者是一样的。
这样的话,所有交易不会跨两天,因此目标就变成了 **贪心策略**:先尽可能使第二天的钱最多,再尽可能使第三天的钱最多,依次类推直到最后一天。
> <font color='red' size=4><b>解读</b></font>:这个为什么是正确的呢?
> 可以用反证法:如果我第$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$。
算法中一共会算 $T1$ 次背包模型,每算一次的时间复杂度是 $O(NM)$,因此总时间复杂度是 $O(TNM)=10^8$。
### 三、实现代码
```cpp {.line-numbers}
#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;
}
```