Skip to content

Commit 46253d3

Browse files
authored
feat: add solutions to lc problem: No.0638 (#3127)
No.0638.Shopping Offers
1 parent ec922e1 commit 46253d3

File tree

7 files changed

+490
-229
lines changed

7 files changed

+490
-229
lines changed

solution/0600-0699/0638.Shopping Offers/README.md

Lines changed: 171 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,23 @@ tags:
7272

7373
<!-- solution:start -->
7474

75-
### 方法一
75+
### 方法一:状态压缩 + 记忆化搜索
76+
77+
我们注意到,题目中物品的种类 $n \leq 6$,而每种物品需要购买的数量不超过 $10$,我们可以用 $4$ 个二进制位来表示每种物品需要购买的数量,这样,我们只需要最多 $6 \times 4 = 24$ 个二进制位来表示整个购物清单。
78+
79+
我们首先将购物清单 $\text{needs}$ 转换为一个整数 $\text{mask}$,其中第 $i$ 个物品需要购买的数量存储在 $\text{mask}$ 的第 $i \times 4$ 位到第 $(i + 1) \times 4 - 1$ 位。例如,当 $\text{needs} = [1, 2, 1]$ 时,有 $\text{mask} = 0b0001 0010 0001$。
80+
81+
然后,我们设计一个函数 $\text{dfs}(cur)$,表示当前购物清单的状态为 $\text{cur}$ 时,我们需要花费的最少金额。那么答案即为 $\text{dfs}(\text{mask})$。
82+
83+
函数 $\text{dfs}(cur)$ 的计算方法如下:
84+
85+
- 我们首先计算当前购物清单 $\text{cur}$ 不使用大礼包时的花费,记为 $\text{ans}$。
86+
- 然后,我们遍历每一个大礼包 $\text{offer}$,如果当前购物清单 $\text{cur}$ 能够使用大礼包 $\text{offer}$,即 $\text{cur}$ 中每种物品的数量都不小于大礼包 $\text{offer}$ 中的数量,那么我们可以尝试使用这个大礼包。我们将 $\text{cur}$ 中每种物品的数量减去大礼包 $\text{offer}$ 中的数量,得到一个新的购物清单 $\text{nxt}$,然后递归计算 $\text{nxt}$ 的最少花费,并加上大礼包的价格 $\text{offer}[n]$,更新 $\text{ans}$,即 $\text{ans} = \min(\text{ans}, \text{offer}[n] + \text{dfs}(\text{nxt}))$。
87+
- 最后,返回 $\text{ans}$。
88+
89+
为了避免重复计算,我们使用一个哈希表 $\text{f}$ 记录每一个状态 $\text{cur}$ 对应的最少花费。
90+
91+
时间复杂度 $O(n \times k \times m^n)$,其中 $n$ 表示物品的种类,而 $k$ 和 $m$ 分别表示大礼包的数量以及每种物品的最大需求量。空间复杂度 $O(n \times m^n)$。
7692

7793
<!-- tabs:start -->
7894

@@ -83,55 +99,72 @@ class Solution:
8399
def shoppingOffers(
84100
self, price: List[int], special: List[List[int]], needs: List[int]
85101
) -> int:
86-
def total(price, needs):
87-
return sum(price[i] * needs[i] for i in range(len(needs)))
88-
89-
ans = total(price, needs)
90-
t = []
91-
for offer in special:
92-
t.clear()
93-
for j in range(len(needs)):
94-
if offer[j] > needs[j]:
95-
t.clear()
96-
break
97-
t.append(needs[j] - offer[j])
98-
if t:
99-
ans = min(ans, offer[-1] + self.shoppingOffers(price, special, t))
100-
return ans
102+
@cache
103+
def dfs(cur: int) -> int:
104+
ans = sum(p * (cur >> (i * bits) & 0xF) for i, p in enumerate(price))
105+
for offer in special:
106+
nxt = cur
107+
for j in range(len(needs)):
108+
if (cur >> (j * bits) & 0xF) < offer[j]:
109+
break
110+
nxt -= offer[j] << (j * bits)
111+
else:
112+
ans = min(ans, offer[-1] + dfs(nxt))
113+
return ans
114+
115+
bits, mask = 4, 0
116+
for i, need in enumerate(needs):
117+
mask |= need << i * bits
118+
return dfs(mask)
101119
```
102120

103121
#### Java
104122

105123
```java
106124
class Solution {
125+
private final int bits = 4;
126+
private int n;
127+
private List<Integer> price;
128+
private List<List<Integer>> special;
129+
private Map<Integer, Integer> f = new HashMap<>();
130+
107131
public int shoppingOffers(
108132
List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
109-
int ans = total(price, needs);
110-
List<Integer> t = new ArrayList<>();
133+
n = needs.size();
134+
this.price = price;
135+
this.special = special;
136+
int mask = 0;
137+
for (int i = 0; i < n; ++i) {
138+
mask |= needs.get(i) << (i * bits);
139+
}
140+
return dfs(mask);
141+
}
142+
143+
private int dfs(int cur) {
144+
if (f.containsKey(cur)) {
145+
return f.get(cur);
146+
}
147+
int ans = 0;
148+
for (int i = 0; i < n; ++i) {
149+
ans += price.get(i) * (cur >> (i * bits) & 0xf);
150+
}
111151
for (List<Integer> offer : special) {
112-
t.clear();
113-
for (int j = 0; j < needs.size(); ++j) {
114-
if (offer.get(j) > needs.get(j)) {
115-
t.clear();
152+
int nxt = cur;
153+
boolean ok = true;
154+
for (int j = 0; j < n; ++j) {
155+
if ((cur >> (j * bits) & 0xf) < offer.get(j)) {
156+
ok = false;
116157
break;
117158
}
118-
t.add(needs.get(j) - offer.get(j));
159+
nxt -= offer.get(j) << (j * bits);
119160
}
120-
if (!t.isEmpty()) {
121-
ans = Math.min(
122-
ans, offer.get(offer.size() - 1) + shoppingOffers(price, special, t));
161+
if (ok) {
162+
ans = Math.min(ans, offer.get(n) + dfs(nxt));
123163
}
124164
}
165+
f.put(cur, ans);
125166
return ans;
126167
}
127-
128-
private int total(List<Integer> price, List<Integer> needs) {
129-
int s = 0;
130-
for (int i = 0; i < price.size(); ++i) {
131-
s += price.get(i) * needs.get(i);
132-
}
133-
return s;
134-
}
135168
}
136169
```
137170

@@ -141,26 +174,39 @@ class Solution {
141174
class Solution {
142175
public:
143176
int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
144-
int ans = total(price, needs);
145-
vector<int> t;
146-
for (auto& offer : special) {
147-
t.clear();
148-
for (int j = 0; j < needs.size(); ++j) {
149-
if (offer[j] > needs[j]) {
150-
t.clear();
151-
break;
177+
const int bits = 4;
178+
int n = needs.size();
179+
unordered_map<int, int> f;
180+
int mask = 0;
181+
for (int i = 0; i < n; ++i) {
182+
mask |= needs[i] << (i * bits);
183+
}
184+
function<int(int)> dfs = [&](int cur) {
185+
if (f.contains(cur)) {
186+
return f[cur];
187+
}
188+
int ans = 0;
189+
for (int i = 0; i < n; ++i) {
190+
ans += price[i] * ((cur >> (i * bits)) & 0xf);
191+
}
192+
for (const auto& offer : special) {
193+
int nxt = cur;
194+
bool ok = true;
195+
for (int j = 0; j < n; ++j) {
196+
if (((cur >> (j * bits)) & 0xf) < offer[j]) {
197+
ok = false;
198+
break;
199+
}
200+
nxt -= offer[j] << (j * bits);
201+
}
202+
if (ok) {
203+
ans = min(ans, offer[n] + dfs(nxt));
152204
}
153-
t.push_back(needs[j] - offer[j]);
154205
}
155-
if (!t.empty()) ans = min(ans, offer[offer.size() - 1] + shoppingOffers(price, special, t));
156-
}
157-
return ans;
158-
}
159-
160-
int total(vector<int>& price, vector<int>& needs) {
161-
int s = 0;
162-
for (int i = 0; i < price.size(); ++i) s += price[i] * needs[i];
163-
return s;
206+
f[cur] = ans;
207+
return ans;
208+
};
209+
return dfs(mask);
164210
}
165211
};
166212
```
@@ -169,37 +215,85 @@ public:
169215
170216
```go
171217
func shoppingOffers(price []int, special [][]int, needs []int) int {
172-
total := func(price, needs []int) int {
173-
s := 0
174-
for i := 0; i < len(needs); i++ {
175-
s += price[i] * needs[i]
176-
}
177-
return s
218+
const bits = 4
219+
n := len(needs)
220+
f := make(map[int]int)
221+
mask := 0
222+
for i, need := range needs {
223+
mask |= need << (i * bits)
178224
}
179225
180-
min := func(a, b int) int {
181-
if a < b {
182-
return a
226+
var dfs func(int) int
227+
dfs = func(cur int) int {
228+
if v, ok := f[cur]; ok {
229+
return v
183230
}
184-
return b
185-
}
186-
187-
ans := total(price, needs)
188-
var t []int
189-
for _, offer := range special {
190-
t = t[:0]
191-
for j := 0; j < len(needs); j++ {
192-
if offer[j] > needs[j] {
193-
t = t[:0]
194-
break
195-
}
196-
t = append(t, needs[j]-offer[j])
231+
ans := 0
232+
for i := 0; i < n; i++ {
233+
ans += price[i] * ((cur >> (i * bits)) & 0xf)
197234
}
198-
if len(t) > 0 {
199-
ans = min(ans, offer[len(offer)-1]+shoppingOffers(price, special, t))
235+
for _, offer := range special {
236+
nxt := cur
237+
ok := true
238+
for j := 0; j < n; j++ {
239+
if ((cur >> (j * bits)) & 0xf) < offer[j] {
240+
ok = false
241+
break
242+
}
243+
nxt -= offer[j] << (j * bits)
244+
}
245+
if ok {
246+
ans = min(ans, offer[n]+dfs(nxt))
247+
}
200248
}
249+
f[cur] = ans
250+
return ans
201251
}
202-
return ans
252+
253+
return dfs(mask)
254+
}
255+
```
256+
257+
#### TypeScript
258+
259+
```ts
260+
function shoppingOffers(price: number[], special: number[][], needs: number[]): number {
261+
const bits = 4;
262+
const n = needs.length;
263+
const f: Map<number, number> = new Map();
264+
265+
let mask = 0;
266+
for (let i = 0; i < n; i++) {
267+
mask |= needs[i] << (i * bits);
268+
}
269+
270+
const dfs = (cur: number): number => {
271+
if (f.has(cur)) {
272+
return f.get(cur)!;
273+
}
274+
let ans = 0;
275+
for (let i = 0; i < n; i++) {
276+
ans += price[i] * ((cur >> (i * bits)) & 0xf);
277+
}
278+
for (const offer of special) {
279+
let nxt = cur;
280+
let ok = true;
281+
for (let j = 0; j < n; j++) {
282+
if (((cur >> (j * bits)) & 0xf) < offer[j]) {
283+
ok = false;
284+
break;
285+
}
286+
nxt -= offer[j] << (j * bits);
287+
}
288+
if (ok) {
289+
ans = Math.min(ans, offer[n] + dfs(nxt));
290+
}
291+
}
292+
f.set(cur, ans);
293+
return ans;
294+
};
295+
296+
return dfs(mask);
203297
}
204298
```
205299

0 commit comments

Comments
 (0)