@@ -222,4 +222,240 @@ function maxTotalReward(rewardValues: number[]): number {
222
222
223
223
<!-- solution: end -->
224
224
225
+ <!-- solution: start -->
226
+
227
+ ### 方法二:动态规划
228
+
229
+ 我们定义 $f[ i] [ j ] $ 表示用前 $i$ 个奖励值,能否得到总奖励 $j$。初始时 $f[ 0] [ 0 ] = \text{True}$,其余值均为 $\text{False}$。
230
+
231
+ 我们考虑第 $i$ 个奖励值 $v$,如果我们不选择它,那么 $f[ i] [ j ] = f[ i - 1] [ j ] $;如果我们选择它,那么 $f[ i] [ j ] = f[ i - 1] [ j - v ] $,其中 $0 \leq j - v \lt v$。即状态转移方程为:
232
+
233
+ $$
234
+ f[i][j] = f[i - 1][j] \vee f[i - 1][j - v]
235
+ $$
236
+
237
+ 最终答案为 $\max\{ j \mid f[ n] [ j ] = \text{True}\} $。
238
+
239
+ 由于 $f[ i] [ j ] $ 只与 $f[ i - 1] [ j ] $ 和 $f[ i - 1] [ j - v ] $ 有关,我们可以优化掉第一维,只使用一个一维数组进行状态转移。
240
+
241
+ 时间复杂度 $O(n \times M)$,空间复杂度 $O(M)$。其中 $n$ 是数组 ` rewardValues ` 的长度,而 $M$ 是数组 ` rewardValues ` 中的最大值的两倍。
242
+
243
+ <!-- tabs: start -->
244
+
245
+ #### Python3
246
+
247
+ ``` python
248
+ class Solution :
249
+ def maxTotalReward (self , rewardValues : List[int ]) -> int :
250
+ nums = sorted (set (rewardValues))
251
+ m = nums[- 1 ] << 1
252
+ f = [False ] * m
253
+ f[0 ] = True
254
+ for v in nums:
255
+ for j in range (m):
256
+ if 0 <= j - v < v:
257
+ f[j] |= f[j - v]
258
+ ans = m - 1
259
+ while not f[ans]:
260
+ ans -= 1
261
+ return ans
262
+ ```
263
+
264
+ #### Java
265
+
266
+ ``` java
267
+ class Solution {
268
+ public int maxTotalReward (int [] rewardValues ) {
269
+ int [] nums = Arrays . stream(rewardValues). distinct(). sorted(). toArray();
270
+ int n = nums. length;
271
+ int m = nums[n - 1 ] << 1 ;
272
+ boolean [] f = new boolean [m];
273
+ f[0 ] = true ;
274
+ for (int v : nums) {
275
+ for (int j = 0 ; j < m; ++ j) {
276
+ if (0 <= j - v && j - v < v) {
277
+ f[j] |= f[j - v];
278
+ }
279
+ }
280
+ }
281
+ int ans = m - 1 ;
282
+ while (! f[ans]) {
283
+ -- ans;
284
+ }
285
+ return ans;
286
+ }
287
+ }
288
+ ```
289
+
290
+ #### C++
291
+
292
+ ``` cpp
293
+ class Solution {
294
+ public:
295
+ int maxTotalReward(vector<int >& rewardValues) {
296
+ sort(rewardValues.begin(), rewardValues.end());
297
+ rewardValues.erase(unique(rewardValues.begin(), rewardValues.end()), rewardValues.end());
298
+ int n = rewardValues.size();
299
+ int m = rewardValues.back() << 1;
300
+ bool f[ m] ;
301
+ memset(f, false, sizeof(f));
302
+ f[ 0] = true;
303
+ for (int v : rewardValues) {
304
+ for (int j = 1; j < m; ++j) {
305
+ if (0 <= j - v && j - v < v) {
306
+ f[ j] = f[ j] || f[ j - v] ;
307
+ }
308
+ }
309
+ }
310
+ int ans = m - 1;
311
+ while (!f[ ans] ) {
312
+ --ans;
313
+ }
314
+ return ans;
315
+ }
316
+ };
317
+ ```
318
+
319
+ #### Go
320
+
321
+ ```go
322
+ func maxTotalReward(rewardValues []int) int {
323
+ slices.Sort(rewardValues)
324
+ nums := slices.Compact(rewardValues)
325
+ n := len(nums)
326
+ m := nums[n-1] << 1
327
+ f := make([]bool, m)
328
+ f[0] = true
329
+ for _, v := range nums {
330
+ for j := 1; j < m; j++ {
331
+ if 0 <= j-v && j-v < v {
332
+ f[j] = f[j] || f[j-v]
333
+ }
334
+ }
335
+ }
336
+ ans := m - 1
337
+ for !f[ans] {
338
+ ans--
339
+ }
340
+ return ans
341
+ }
342
+ ```
343
+
344
+ #### TypeScript
345
+
346
+ ``` ts
347
+ function maxTotalReward(rewardValues : number []): number {
348
+ const nums = Array .from (new Set (rewardValues )).sort ((a , b ) => a - b );
349
+ const n = nums .length ;
350
+ const m = nums [n - 1 ] << 1 ;
351
+ const f: boolean [] = Array (m ).fill (false );
352
+ f [0 ] = true ;
353
+ for (const v of nums ) {
354
+ for (let j = 1 ; j < m ; ++ j ) {
355
+ if (0 <= j - v && j - v < v ) {
356
+ f [j ] = f [j ] || f [j - v ];
357
+ }
358
+ }
359
+ }
360
+ let ans = m - 1 ;
361
+ while (! f [ans ]) {
362
+ -- ans ;
363
+ }
364
+ return ans ;
365
+ }
366
+ ```
367
+
368
+ <!-- tabs: end -->
369
+
370
+ <!-- solution: end -->
371
+
372
+ <!-- solution: start -->
373
+
374
+ ### 方法三:动态规划 + 位运算
375
+
376
+ 我们可以对方法二进行优化,定义一个二进制数 $f$ 保存当前的状态,其中 $f$ 的第 $i$ 位为 $1$ 表示当前总奖励为 $i$ 是可达的。
377
+
378
+ 观察方法二的状态转移方程 $f[ j] = f[ j] \vee f[ j - v] $,这相当于取 $f$ 的低 $v$ 位,再左移 $v$ 位,然后与原来的 $f$ 进行或运算。
379
+
380
+ 那么答案为 $f$ 的最高位的位置。
381
+
382
+ 时间复杂度 $O(n \times M / w)$,空间复杂度 $O(n + M / w)$。其中 $n$ 是数组 ` rewardValues ` 的长度,而 $M$ 是数组 ` rewardValues ` 中的最大值的两倍。整数 $w = 32$ 或 $64$。
383
+
384
+ <!-- tabs: start -->
385
+
386
+ #### Python3
387
+
388
+ ``` python
389
+ class Solution :
390
+ def maxTotalReward (self , rewardValues : List[int ]) -> int :
391
+ nums = sorted (set (rewardValues))
392
+ f = 1
393
+ for v in nums:
394
+ f |= (f & ((1 << v) - 1 )) << v
395
+ return f.bit_length() - 1
396
+ ```
397
+
398
+ #### Java
399
+
400
+ ``` java
401
+ import java.math.BigInteger ;
402
+ import java.util.Arrays ;
403
+
404
+ class Solution {
405
+ public int maxTotalReward (int [] rewardValues ) {
406
+ int [] nums = Arrays . stream(rewardValues). distinct(). sorted(). toArray();
407
+ BigInteger f = BigInteger . ONE ;
408
+ for (int v : nums) {
409
+ BigInteger mask = BigInteger . ONE. shiftLeft(v). subtract(BigInteger . ONE );
410
+ BigInteger shifted = f. and(mask). shiftLeft(v);
411
+ f = f. or(shifted);
412
+ }
413
+ return f. bitLength() - 1 ;
414
+ }
415
+ }
416
+ ```
417
+
418
+ #### C++
419
+
420
+ ``` cpp
421
+ class Solution {
422
+ public:
423
+ int maxTotalReward(vector<int >& rewardValues) {
424
+ sort(rewardValues.begin(), rewardValues.end());
425
+ rewardValues.erase(unique(rewardValues.begin(), rewardValues.end()), rewardValues.end());
426
+ bitset<100000> f{1};
427
+ for (int v : rewardValues) {
428
+ int shift = f.size() - v;
429
+ f |= f << shift >> (shift - v);
430
+ }
431
+ for (int i = rewardValues.back() * 2 - 1;; i--) {
432
+ if (f.test(i)) {
433
+ return i;
434
+ }
435
+ }
436
+ }
437
+ };
438
+ ```
439
+
440
+ #### Go
441
+
442
+ ```go
443
+ func maxTotalReward(rewardValues []int) int {
444
+ slices.Sort(rewardValues)
445
+ rewardValues = slices.Compact(rewardValues)
446
+ one := big.NewInt(1)
447
+ f := big.NewInt(1)
448
+ p := new(big.Int)
449
+ for _, v := range rewardValues {
450
+ mask := p.Sub(p.Lsh(one, uint(v)), one)
451
+ f.Or(f, p.Lsh(p.And(f, mask), uint(v)))
452
+ }
453
+ return f.BitLen() - 1
454
+ }
455
+ ```
456
+
457
+ <!-- tabs: end -->
458
+
459
+ <!-- solution: end -->
460
+
225
461
<!-- problem: end -->
0 commit comments