Luogu-P5662-纪念品

题目 P5662 [CSP-J2019] 纪念品

题目描述

小伟突然获得一种超能力,他知道未来 TTNN 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。

每天,小伟可以进行以下两种交易无限次

  1. 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
  2. 卖出持有的任意一个纪念品,以当日价格换回金币。

每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。

TT 天之后,小伟的超能力消失。因此他一定会在第 TT 天卖出所有纪念品换回金币。

小伟现在有 MM 枚金币,他想要在超能力消失后拥有尽可能多的金币。

输入格式

第一行包含三个正整数 T,N,MT, N, M,相邻两数之间以一个空格分开,分别代表未来天数 TT,纪念品数量 NN,小伟现在拥有的金币数量 MM

接下来 TT 行,每行包含 NN 个正整数,相邻两数之间以一个空格分隔。第 ii 行的 NN 个正整数分别为 Pi,1,Pi,2,,Pi,NP_{i,1},P_{i,2},\dots,P_{i,N},其中 Pi,jP_{i,j} 表示第 ii 天第 jj 种纪念品的价格。

输出格式

输出仅一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。

思路:dp-完全背包

使用完全背包原因

  • 无限购买

  • 纪念品可以和钱任意转换(允许同时买入卖出)

  • 每天开始时,

    • \rightarrow 容量
    • 今天的价格 \rightarrow 花费 cost[i]cost[i]
    • 明天的价格 \rightarrow 价值 value[i]value[i]

dp表

和正常的完全背包一样,只不过 dp[k]dp[k] 表示钱的量为 kk 时的最大利润(不含成本)。含成本的方案见补充部分。

状态

  • 初状态:均为 00

  • 状态转移方程:(此后均用 minset/maxset(x,y)\mathrm{minset}/\mathrm{maxset}(x, y) 代替 x=min/max(x,y)x = \min/\max(x, y)

    1
    2
    3
    maxset(dp[k], dp[k - price[i][j]] + price[i + 1][j] - price[i + 1][j]);

    money += dp[money];
  • 末状态:最后的 moneymoney

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// template v7
#include <bits/stdc++.h>

using namespace std;
using ll = long long;

inline void minset(int &t, int other) {
t = min(t, other);
}

inline void maxset(int &t, int other) {
t = max(t, other);
}

const int inf = 0x7f7f7f7f;

const int N = 110;
const int M = 1e4+10;

int price[N][N];

int dp[M];

int main() {

int t, n, m;
cin >> t >> n >> m;
for(int i = 1; i <= t; i++) {
for(int j = 1; j <= n; j++) {
cin >> price[i][j];
}
}
int money = m;
for(int i = 1; i < t; i++) {
memset(dp, 0, sizeof dp);
for(int j = 1; j <= n; j++) {
for(int k = price[i][j]; k <= money; k++){
maxset(dp[k], dp[k - price[i][j]] + price[i + 1][j] - price[i][j]);
}
}
money += dp[money];
}
cout << money << endl;
return 0;
}

补充

使用含成本的dp

含成本的状态

  • 初状态:均为 moneymoney

  • 状态转移方程:

    1
    2
    3
    maxset(dp[k], dp[k - price[i][j]] + price[i + 1][j] - price[i + 1][j]);

    money = dp[money];
  • 末状态:最后的 moneymoney

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// template v7
#include <bits/stdc++.h>

using namespace std;
using ll = long long;

inline void minset(int &t, int other) {
t = min(t, other);
}

inline void maxset(int &t, int other) {
t = max(t, other);
}

const int inf = 0x7f7f7f7f;

const int N = 110;
const int M = 1e4+10;

int price[N][N];

int dp[M];

int main() {

int t, n, m;
cin >> t >> n >> m;
for(int i = 1; i <= t; i++) {
for(int j = 1; j <= n; j++) {
cin >> price[i][j];
}
}
int money = m;
for(int i = 1; i < t; i++) {
memset(dp, 0, sizeof dp);
for(int k = 0; k <= money; k++) {
dp[k] = money;
}
for(int j = 1; j <= n; j++) {
for(int k = price[i][j]; k <= money; k++){
maxset(dp[k], dp[k - price[i][j]] + price[i + 1][j] - price[i][j]);
}
}
money = dp[money];
}
cout << money << endl;
return 0;
}