lc638
dfs+memo
比"不买任何大礼包单买商品"和"买各个可用大礼包后再买剩余商品"的花费,找出满足购物需求的最低价格。
class Solution {
public:
// 不同的needs所需的价格
map<vector<int>, int> _cache;
int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs)
{
return dfs(needs, price, special);
}
int dfs(vector<int> needs, vector<int>& price, vector<vector<int>>& special) {
// 如果子问题已经计算过 直接返回
if (_cache[needs]) { return _cache[needs]; }
int ans = 0;
// 最弱的方式;不购买大礼包
for (int i = 0; i < needs.size(); i++) {
ans += needs[i] * price[i];
}
// 遍历每个礼包,购买它,看看是不是能获得更便宜的价格
for (int i = 0; i < special.size(); i++) {
vector<int> next = needs;
bool valid = true;
// 因为购买的数量需要正好是needs 所以大礼包的某个商品不能超过needs的商品数量
for (int item = 0; item < price.size(); item++) {
if (special[i][item] > needs[item]) {
valid = false;
break;
}
}
// 当前大礼包不符合要求 跳过
if (!valid) continue;
// 当前大礼包符合要求,用next数组记录买过大礼包之后还需要买多少商品
for (int item = 0; item < price.size(); item++) {
++next[item] -= special[i][item];++
}
++ans = min(ans, dfs(next, price, special) + special[i].back());++
}
// 更新cache
_cache[needs] = ans;
return ans;
}
};
lc473
先判断火柴总长度是否能分成4等份且无超长火柴
再用回溯尝试把每根火柴分配到正方形的四条边,看是否能让每条边长度都等于目标边长
++// 剪枝:避免重复探索相同长度火柴的无效分支
if (i > 0 && matchsticks[index] == matchsticks[index - 1] && !vis[index - 1]) {
continue;++
class Solution {
public:
bool makesquare(vector<int>& matchsticks) {
int sum = 0;
for (int m : matchsticks) sum += m;
if (sum % 4 != 0) return false;
int target = sum / 4;
sort(matchsticks.rbegin(), matchsticks.rend());
for (int m : matchsticks) if (m > target) return false;
vector<int> edges(4, 0);
vector<bool> vis(matchsticks.size(), false); // 标记火柴是否已使用
return backtrack(matchsticks, edges, 0, target, vis);
}
bool backtrack(vector<int>& matchsticks, vector<int>& edges, int index, int target, vector<bool>& vis)
{
if (index == matchsticks.size()) return true;
for (int i = 0; i < 4; ++i) {
// 剪枝1:当前边放入火柴后长度不超过target
if (edges[i] + matchsticks[index] <= target) {
++// 剪枝2:避免重复探索相同长度火柴的无效分支
if (i > 0 && matchsticks[index] == matchsticks[index - 1] && !vis[index - 1]) {
continue;++
}
++edges[i] += matchsticks[index];++
vis[index] = true;
if (backtrack(matchsticks, edges, ++index + 1,++ target, vis))
return true;
edges[i] -= matchsticks[index];
vis[index] = false;
}
// 剪枝3:当前边长度为0,后续边无需重复处理
if (edges[i] == 0) break;
}
return false;
}
};