Skip to content

feat: add solutions to lc problem: No.0638 #3127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 171 additions & 77 deletions solution/0600-0699/0638.Shopping Offers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,23 @@ tags:

<!-- solution:start -->

### 方法一
### 方法一:状态压缩 + 记忆化搜索

我们注意到,题目中物品的种类 $n \leq 6$,而每种物品需要购买的数量不超过 $10$,我们可以用 $4$ 个二进制位来表示每种物品需要购买的数量,这样,我们只需要最多 $6 \times 4 = 24$ 个二进制位来表示整个购物清单。

我们首先将购物清单 $\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$。

然后,我们设计一个函数 $\text{dfs}(cur)$,表示当前购物清单的状态为 $\text{cur}$ 时,我们需要花费的最少金额。那么答案即为 $\text{dfs}(\text{mask})$。

函数 $\text{dfs}(cur)$ 的计算方法如下:

- 我们首先计算当前购物清单 $\text{cur}$ 不使用大礼包时的花费,记为 $\text{ans}$。
- 然后,我们遍历每一个大礼包 $\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}))$。
- 最后,返回 $\text{ans}$。

为了避免重复计算,我们使用一个哈希表 $\text{f}$ 记录每一个状态 $\text{cur}$ 对应的最少花费。

时间复杂度 $O(n \times k \times m^n)$,其中 $n$ 表示物品的种类,而 $k$ 和 $m$ 分别表示大礼包的数量以及每种物品的最大需求量。空间复杂度 $O(n \times m^n)$。

<!-- tabs:start -->

Expand All @@ -83,55 +99,72 @@ class Solution:
def shoppingOffers(
self, price: List[int], special: List[List[int]], needs: List[int]
) -> int:
def total(price, needs):
return sum(price[i] * needs[i] for i in range(len(needs)))

ans = total(price, needs)
t = []
for offer in special:
t.clear()
for j in range(len(needs)):
if offer[j] > needs[j]:
t.clear()
break
t.append(needs[j] - offer[j])
if t:
ans = min(ans, offer[-1] + self.shoppingOffers(price, special, t))
return ans
@cache
def dfs(cur: int) -> int:
ans = sum(p * (cur >> (i * bits) & 0xF) for i, p in enumerate(price))
for offer in special:
nxt = cur
for j in range(len(needs)):
if (cur >> (j * bits) & 0xF) < offer[j]:
break
nxt -= offer[j] << (j * bits)
else:
ans = min(ans, offer[-1] + dfs(nxt))
return ans

bits, mask = 4, 0
for i, need in enumerate(needs):
mask |= need << i * bits
return dfs(mask)
```

#### Java

```java
class Solution {
private final int bits = 4;
private int n;
private List<Integer> price;
private List<List<Integer>> special;
private Map<Integer, Integer> f = new HashMap<>();

public int shoppingOffers(
List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
int ans = total(price, needs);
List<Integer> t = new ArrayList<>();
n = needs.size();
this.price = price;
this.special = special;
int mask = 0;
for (int i = 0; i < n; ++i) {
mask |= needs.get(i) << (i * bits);
}
return dfs(mask);
}

private int dfs(int cur) {
if (f.containsKey(cur)) {
return f.get(cur);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += price.get(i) * (cur >> (i * bits) & 0xf);
}
for (List<Integer> offer : special) {
t.clear();
for (int j = 0; j < needs.size(); ++j) {
if (offer.get(j) > needs.get(j)) {
t.clear();
int nxt = cur;
boolean ok = true;
for (int j = 0; j < n; ++j) {
if ((cur >> (j * bits) & 0xf) < offer.get(j)) {
ok = false;
break;
}
t.add(needs.get(j) - offer.get(j));
nxt -= offer.get(j) << (j * bits);
}
if (!t.isEmpty()) {
ans = Math.min(
ans, offer.get(offer.size() - 1) + shoppingOffers(price, special, t));
if (ok) {
ans = Math.min(ans, offer.get(n) + dfs(nxt));
}
}
f.put(cur, ans);
return ans;
}

private int total(List<Integer> price, List<Integer> needs) {
int s = 0;
for (int i = 0; i < price.size(); ++i) {
s += price.get(i) * needs.get(i);
}
return s;
}
}
```

Expand All @@ -141,26 +174,39 @@ class Solution {
class Solution {
public:
int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
int ans = total(price, needs);
vector<int> t;
for (auto& offer : special) {
t.clear();
for (int j = 0; j < needs.size(); ++j) {
if (offer[j] > needs[j]) {
t.clear();
break;
const int bits = 4;
int n = needs.size();
unordered_map<int, int> f;
int mask = 0;
for (int i = 0; i < n; ++i) {
mask |= needs[i] << (i * bits);
}
function<int(int)> dfs = [&](int cur) {
if (f.contains(cur)) {
return f[cur];
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += price[i] * ((cur >> (i * bits)) & 0xf);
}
for (const auto& offer : special) {
int nxt = cur;
bool ok = true;
for (int j = 0; j < n; ++j) {
if (((cur >> (j * bits)) & 0xf) < offer[j]) {
ok = false;
break;
}
nxt -= offer[j] << (j * bits);
}
if (ok) {
ans = min(ans, offer[n] + dfs(nxt));
}
t.push_back(needs[j] - offer[j]);
}
if (!t.empty()) ans = min(ans, offer[offer.size() - 1] + shoppingOffers(price, special, t));
}
return ans;
}

int total(vector<int>& price, vector<int>& needs) {
int s = 0;
for (int i = 0; i < price.size(); ++i) s += price[i] * needs[i];
return s;
f[cur] = ans;
return ans;
};
return dfs(mask);
}
};
```
Expand All @@ -169,37 +215,85 @@ public:

```go
func shoppingOffers(price []int, special [][]int, needs []int) int {
total := func(price, needs []int) int {
s := 0
for i := 0; i < len(needs); i++ {
s += price[i] * needs[i]
}
return s
const bits = 4
n := len(needs)
f := make(map[int]int)
mask := 0
for i, need := range needs {
mask |= need << (i * bits)
}

min := func(a, b int) int {
if a < b {
return a
var dfs func(int) int
dfs = func(cur int) int {
if v, ok := f[cur]; ok {
return v
}
return b
}

ans := total(price, needs)
var t []int
for _, offer := range special {
t = t[:0]
for j := 0; j < len(needs); j++ {
if offer[j] > needs[j] {
t = t[:0]
break
}
t = append(t, needs[j]-offer[j])
ans := 0
for i := 0; i < n; i++ {
ans += price[i] * ((cur >> (i * bits)) & 0xf)
}
if len(t) > 0 {
ans = min(ans, offer[len(offer)-1]+shoppingOffers(price, special, t))
for _, offer := range special {
nxt := cur
ok := true
for j := 0; j < n; j++ {
if ((cur >> (j * bits)) & 0xf) < offer[j] {
ok = false
break
}
nxt -= offer[j] << (j * bits)
}
if ok {
ans = min(ans, offer[n]+dfs(nxt))
}
}
f[cur] = ans
return ans
}
return ans

return dfs(mask)
}
```

#### TypeScript

```ts
function shoppingOffers(price: number[], special: number[][], needs: number[]): number {
const bits = 4;
const n = needs.length;
const f: Map<number, number> = new Map();

let mask = 0;
for (let i = 0; i < n; i++) {
mask |= needs[i] << (i * bits);
}

const dfs = (cur: number): number => {
if (f.has(cur)) {
return f.get(cur)!;
}
let ans = 0;
for (let i = 0; i < n; i++) {
ans += price[i] * ((cur >> (i * bits)) & 0xf);
}
for (const offer of special) {
let nxt = cur;
let ok = true;
for (let j = 0; j < n; j++) {
if (((cur >> (j * bits)) & 0xf) < offer[j]) {
ok = false;
break;
}
nxt -= offer[j] << (j * bits);
}
if (ok) {
ans = Math.min(ans, offer[n] + dfs(nxt));
}
}
f.set(cur, ans);
return ans;
};

return dfs(mask);
}
```

Expand Down
Loading
Loading