598 9527 9527
104 14
571 187
874 31
165 89
65 4
878 122
882 152
732 503
124 25
38 17
531
<4993 bytes omitted>
用户输出
99
系统信息
Exited with return code 0
编号 | 题目 | 状态 | 分数 | 总时间 | 内存 | 代码 / 答案文件 | 提交者 | 提交时间 |
---|---|---|---|---|---|---|---|---|
#63484 | #1290. JvJv与夏季特卖 | Accepted | 100 | 69 ms | 476 K | C++ / 4.9 K | ining | 2021-08-08 17:09:34 |
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int long long
const int N = 1e4 + 10;
const int INF = 0x3f3f3f3f;
int a[N], b[N]; // a数组存的是原价,b数组存的是折后价
int dp[N]; // dp[i]表示的是在花费i价钱的情况下享受的最少折扣
void solve() {
int n, L, R;
cin >> n >> L >> R; //变量名和题意相同
for (int i = 1; i <= n; ++i) cin >> a[i] >> b[i]; //把每一种游戏当作一个物品,后面用“物品”称呼
memset(dp, INF, sizeof dp); //因为题目要求的是最小值,所以应该初始化为一个极大值
dp[0] = 0; //这表示的是在不花钱的时候也不享受折扣
// dp的本质是从上一个状态转移到下一个状态,当我们全部都初始化为INF的时候,这就没有状态转移的开始了
//所以我们会在一个能够确定的地方对数值进行初始化,让状态得以开始进行转移
//常见的一般是什么都没有的时候为0啊,为1,左右端点等特殊情况,这个要根据题意进行设定
for (int i = 1; i <= n; ++i) //枚举每一个物品的使用情况
for (int j = R; j >= b[i]; --j)
//因为花费的上限是R,所以我们从R开始枚举;为什么下限是b[i]呢,因为我们后面的数组下标为j-b[i],我们不能下标越界了
dp[j] = min(dp[j], dp[j - b[i]] + (a[i] - b[i])); //更新最小值
//第二层的for会有很多内容,我会在下面分点说明,如果有不理解的地方可以试着先整体看完,说不定就悟了
/*
* 1. 状态转移方程 dp[j] = min(dp[j], dp[j - b[i]] + (a[i] - b[i]));
min里面的dp[j]就是因为我们嘶进行不断的更新,这个很好理解
dp[j - b[i]] + (a[i] - b[i]) 表示的意思是我们现在购买了物品i,
此时我们需要花费b[i]这个价钱,花费了之后我们现在的
钱就变成了j-b[i],那为我们享受的折扣也增加了,增加的是物品i的a[i]-b[i]。
这就是一个状态转移,在现有花费为j的情况下,对于物品i我们有两种状态,一种是不买的dp[j],一种是购买物品i的dp[j
- b[i]] + (a[i] - b[i]),我们不断的取min更新这一数值
所谓的01背包,就是题目给定了n种物品和一个容量有限的背包(空间),每件物品都会消耗背包的一定容积(对应的是题目中的花费),
并带来一定价值(对应题目中的折扣),要求如何选取装入背包中的物品,使得背包内的物品价值最大or小。
* 2. 第二层的for为什么是逆序的for呢,这个好难讲啊md
可能看到上面的代码你会有疑问(哦可能根本没有),就是为什么我们的dp数组,没有维度取表示当前是第几个物品呢
其实它最原始的状态转移是dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - b[i]] + (a[i] - b[i]))
这么一看是不是一下子就理解的状态转移方程了
物品i只和上一个物品(也就是物品i-1)有关,在第i个物品且花费了j的情况下,①我们可以选择不买当前第i个物品,也就是dp[i
- 1][j];
②还可以选择花费b[i]购买当前物品,因为dp[i][j]表示的是做完选择之后的状态,所以在上一个状态的时候的花费应该是j
- b[i],这样在选择购买 此次物品之后的花费才会变成j,与此同时我们的价值(也就是题目中说的折扣)会加上a[i] -
b[i],这就是dp[i - 1][j - b[i]] + (a[i] - b[i]) 的含义。
理解了更本质的状态转移方程之后,我们将在此引入"滚动数组"的含义,它将把我们的二维dp数组压到一维进行
我们不难理解,物品i的所有状态,都之和i-1有关,也就是说我们只需要用一维数组来保存i-1的状态和
假设i-1时刻的dp[]是{a0, a1, a2... an}, 那么对于i���刻的第x个数就应该是min{dp[x], dp[x-c[x]]+w[x]}
c[x]是物品的花费,对应题目中物品x的花费
w[x]是价值,对应题目中物品x的折扣
(min和max是根据题目确定,这里写min是因为题目要求的是min)
这时候就可以知道为什么需要我们逆序的去遍历dp数组了,因为只有这样才能保证物品i时刻所进行转移的前一个状态是i-1的
如果我们是进行正序的遍历,那么dp[0],dp[1]...都已经被修改过了,等于说我们会用物品i更新物品i,那这样就错了
*/
int ans = INF;
for (int i = L; i <= R; ++i) ans = min(ans, dp[i]); //经过了以上的注释,这里就很容易理解了
cout << (ans == INF ? -1 : ans) << '\n';
//在dp中,正序和逆序的遍历十分重要,这关系到了状态是从合适转移到当前的状态的,需要好好理解
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
solve();
return 0;
}
598 9527 9527
104 14
571 187
874 31
165 89
65 4
878 122
882 152
732 503
124 25
38 17
531
<4993 bytes omitted>
用户输出
99
系统信息
Exited with return code 0
372 705 1629
643 551
222 190
276 101
651 493
838 734
831 348
746 682
773 649
22 14
148 111
<3077 bytes omitted>
用户输出
11
系统信息
Exited with return code 0
405 2470 2470
902 254
913 239
253 26
948 311
63 23
115 103
309 294
494 475
216 59
244 214
<3368 bytes omitted>
用户输出
30
系统信息
Exited with return code 0
724 2360 4483
134 88
870 47
714 678
350 246
873 689
282 190
259 3
867 38
345 126
761 107
<6063 bytes omitted>
用户输出
5
系统信息
Exited with return code 0
88 4046 6345
277 84
660 169
800 104
763 49
432 356
145 19
227 117
67 48
388 353
77 0
421
<653 bytes omitted>
用户输出
288
系统信息
Exited with return code 0
677 5464 6403
579 331
162 134
374 326
725 542
161 17
789 246
912 639
396 40
666 277
308 25
<5680 bytes omitted>
用户输出
59
系统信息
Exited with return code 0
287 0 1927
104 62
944 28
284 238
114 15
172 55
583 549
546 217
741 176
183 82
569 299
212
<2361 bytes omitted>
用户输出
0
系统信息
Exited with return code 0
895 6166 6900
74 66
995 245
190 148
478 267
978 18
190 21
359 206
944 917
930 897
989 897
<7530 bytes omitted>
用户输出
30
系统信息
Exited with return code 0
用户输出
-1
系统信息
Exited with return code 0
305 5892 8713
127 59
293 178
866 463
766 148
949 601
462 146
982 837
403 187
457 175
220 2
<2518 bytes omitted>
用户输出
198
系统信息
Exited with return code 0
289 3443 6250
987 498
980 631
959 882
194 3
531 205
797 293
6 4
142 43
559 478
308 141
75
<2366 bytes omitted>
用户输出
63
系统信息
Exited with return code 0
970 3243 4686
437 110
236 166
35 16
646 103
112 28
287 235
430 191
700 254
949 316
469 358
<8169 bytes omitted>
用户输出
32
系统信息
Exited with return code 0
762 480 1906
532 32
911 265
223 68
930 184
829 779
124 33
738 667
76 75
680 570
94 32
369
<6380 bytes omitted>
用户输出
0
系统信息
Exited with return code 0
543 5615 8194
538 493
688 425
457 355
253 246
455 12
558 480
414 60
565 226
231 41
199 31
<4519 bytes omitted>
用户输出
65
系统信息
Exited with return code 0