This commit is contained in:
youngyangyang04
2020-12-12 17:37:38 +08:00
parent 17f8422daf
commit 0aaf6dd4b8
5 changed files with 208 additions and 1 deletions

View File

@ -1,6 +1,11 @@
和Leetcode 473火柴拼正方形和Leetcode 698划分为k个相等的子集是
* 473. 火柴拼正方形 (回溯算法)
* 698. 划分为k个相等的子集
一起再回忆一下回溯算法
|[0473.火柴拼正方形](https://github.com/youngyangyang04/leetcode/blob/master/problems/0473.火柴拼正方形.md) |深度优先搜索|中等| **回溯算法** 和698.划分为k个相等的子集差不多|
## 思路
@ -82,6 +87,8 @@ public:
本来是想用回溯暴力搜索出所有答案的各种剪枝还是超时了不想在调了放弃回溯直接上01背包吧。
**需要尝试一下记忆化递归**
回溯搜索超时的代码如下:
```
@ -119,3 +126,45 @@ public:
};
```
```
class Solution {
private:
bool backtracking(vector<int>& nums,
int k,
int target, // 子集目标和
int cur, // 当前目标和
int startIndex, // 起始位置
vector<bool>& used) { // 标记是否使用过
if (k == 0) return true; // 找到了k个相同子集
if (cur == target) { // 发现一个合格子集,然后重新开始寻找
return backtracking(nums, k - 1, target, 0, 0, used); // k-1
}
for (int i = startIndex; i < nums.size(); i++) {
if (cur + nums[i] <= target && !used[i]) {
used[i] = true;
if (backtracking(nums, k, target, cur + nums[i], i + 1, used)) {
return true;
}
used[i] = false;
}
}
return false;
}
public:
bool canPartition(vector<int>& nums) {
if (nums.size() < 2) return false; // 火柴数量小于4凑不上正方形
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
if (sum % 2 != 0) return false;
int target = sum / 2;
vector<bool> used(nums.size(), false);
return backtracking(nums, 2, target, 0, 0, used);
}
};
```

View File

@ -0,0 +1,42 @@
698.划分为k个相等的子集 的代码几乎不用改动就可以AC
```
class Solution {
private:
bool backtracking(vector<int>& nums,
int k,
int target, // 子集目标和
int cur, // 当前目标和
int startIndex, // 起始位置
vector<bool>& used) { // 标记是否使用过
if (k == 0) return true; // 找到了k个相同子集
if (cur == target) { // 发现一个合格子集,然后重新开始寻找
return backtracking(nums, k - 1, target, 0, 0, used); // k-1
}
for (int i = startIndex; i < nums.size(); i++) {
if (cur + nums[i] <= target && !used[i]) {
used[i] = true;
if (backtracking(nums, k, target, cur + nums[i], i + 1, used)) {
return true;
}
used[i] = false;
}
}
return false;
}
public:
bool makesquare(vector<int>& nums) {
if (nums.size() < 4) return false; // 火柴数量小于4凑不上正方形
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
if (sum % 4 != 0) return false;
int target = sum / 4;
vector<bool> used(nums.size(), false);
return backtracking(nums, 4, target, 0, 0, used);
}
};
```

View File

@ -0,0 +1,70 @@
## 思路
这道题 题意太绕了,我举一个更形象的例子给大家捋顺一下。
例如输入"RRDDD",执行过程应该是什么样呢?
* 第一轮senate[0]的R消灭senate[2]的Dsenate[1]的R消灭senate[3]的Dsenate[4]的D消灭senate[0]的R此时剩下"RD",第一轮结束!
* 第二轮senate[0]的R消灭senate[1]的D第二轮结束
* 第三轮只有R了R胜利
估计不少同学都困惑R和D数量相同怎么办究竟谁赢**其实这是一个持续消灭的过程!** 即如果同时存在R和D就继续进行下一轮消灭轮数直到只剩下R或者D为止
那么每一轮消灭的策略应该是什么呢?
例如RDDRD
第一轮senate[0]的R消灭senate[1]的D那么senate[2]的D是消灭senate[0]的R还是消灭senate[3]的R呢
当然是消灭senate[3]的R因为当轮到这个R的时候它可以消灭senate[4]的D。
**所以消灭的策略是,尽量消灭自己后面的对手,因为前面的对手已经使用过权利了,而后序的对手依然可以使用权利消灭自己的同伴!**
那么局部最优:有一次权利机会,就消灭自己后面的对手。全局最优:为自己的阵营赢取最大利益。
局部最优可以退出全局最优,举不出反例,那么试试贪心。
如果对贪心算法理论基础还不了解的话,可以看看这篇:[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/O935TaoHE9Eexwe_vSbRAg) ,相信看完之后对贪心就有基本的了解了。
## 代码实现
实现代码,在每一轮循环的过程中,去过模拟优先消灭身后的对手,其实是比较麻烦的。
这里有一个技巧就是用一个变量记录当前参议员之前有几个敌对对手了进而判断自己是否被消灭了。这个变量我用flag来表示。
C++代码如下:
```
class Solution {
public:
string predictPartyVictory(string senate) {
// R = true表示本轮循环结束后字符串里依然有R。D同理
bool R = true, D = true;
// 当flag大于0时R在D前出现R可以消灭D。当flag小于0时D在R前出现D可以消灭R
int flag = 0;
while (R && D) { // 一旦R或者D为false就结束循环说明本轮结束后只剩下R或者D了
R = false;
D = false;
for (int i = 0; i < senate.size(); i++) {
if (senate[i] == 'R') {
if (flag < 0) senate[i] = 0; // 消灭RR此时为false
else R = true; // 如果没被消灭本轮循环结束有R
flag++;
}
if (senate[i] == 'D') {
if (flag > 0) senate[i] = 0;
else D = true;
flag--;
}
}
}
// 循环结束之后R和D只能有一个为true
return R == true ? "Radiant" : "Dire";
}
};
```
**如果感觉题解对你有帮助,不要吝啬给一个👍吧!**

View File

@ -0,0 +1,42 @@
使用回溯法首先要明确这是组合问题即集合里是不强调顺序的所以要用startIndex
```
class Solution {
private:
bool backtracking(vector<int>& nums,
int k,
int target, // 子集目标和
int cur, // 当前目标和
int startIndex, // 起始位置
vector<bool>& used) { // 标记是否使用过
if (k == 0) return true; // 找到了k个相同子集
if (cur == target) { // 发现一个合格子集,然后重新开始寻找
return backtracking(nums, k - 1, target, 0, 0, used); // k-1
}
for (int i = startIndex; i < nums.size(); i++) {
if (cur + nums[i] <= target && !used[i]) {
used[i] = true;
if (backtracking(nums, k, target, cur + nums[i], i + 1, used)) {
return true;
}
used[i] = false;
}
}
return false;
}
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
//sort(nums.begin(), nums.end()); 不需要排序
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
if (sum % k != 0) return false;
int target = sum / k;
vector<bool> used(nums.size(), false);
return backtracking(nums, k, target, 0, 0, used);
}
};
```