题目描述
给定一些整数,代表火柴棍的长度。求这些火柴棍是否可以组成一个正方形。火柴棍不可以拆分,但是可以拼接。
样例
Input: [1,1,2,2,2]
Output: true
算法
(深度优先搜索) $O(4^n)$
很明显正方形四条边长一样。
因此我们可以求出边长。
接下来设置一个SubSum的数组,长度为4,表明当前状态下每条边长的长度。
我们在深度优先搜索的过程中枚举每根木棍放在第几个边长上。 最后只要前三个边长的长度等于给定的边长,那么剩下的第四个边长一定也和一样。(当然木棍总和如果不是4的倍数要先剔除)
一点优化: 深度优先搜索在寻找的过程中, 深度尽可能的浅时间比较短, 那么我们就将长的木棍放在前面,短的放在后面即可。
时间复杂度分析:每次枚举长度为4,共有n层,故复杂度为$O(4^n)$
C++ 代码
bool cmp(int x,int y){
return x>y;
}
class Solution {
public:
bool makesquare(vector<int>& nums) {
if (nums.size() == 0) return false;
int sum = 0;
for (int i = 0;i<nums.size();++i){
sum += nums[i];
}
int len = sum/4;
if (len*4 != sum)
return false;
sort(nums.begin(),nums.end(),cmp);
vector<int> SubSum(4);
bool res = dfs(nums,SubSum,len,0);
return res;
}
bool dfs(vector<int>&nums,vector<int>SubSum,int len,int index){
if (SubSum[0] == len && SubSum[1] == len && SubSum[2] == len){
return true;
}
for (int i = 0;i<4;++i){
if (SubSum[i]+nums[index]> len) continue;
SubSum[i] += nums[index];
if (dfs(nums,SubSum,len,index+1)) return true;
SubSum[i] -= nums[index];
}
return false;
}
};
这个代码现在TLE了,
可以把dfs里面判断为true的地方优化如下:
这样就能过了
在搜索恢复现场后,其实有两种情况可以剪枝了。
- 如果在
cur=0
的时候就失败了,直接返回false,说明还没有使用的最长的火柴找不到可匹配的组合。- 如果
cur + nums[i] = target
的时候匹配失败,直接返回false,这是因为我们已经将火柴降序排列,那么后序搜索的火柴和当前cur
的和要小于target
。厉害
火柴棒个数超过32,state压位要出问题
嗯嗯,可以使用bitset或者long long ,一般这种题目不会范围出得很大。因为本质上还是爆搜。
请教一下,“这是因为我们已经将火柴降序排列,那么后序搜索的火柴和当前cur的和要小于target。”但是后续搜索还有很多小于i的元素可以选择呀,不一定就凑不出来target 呀 ==,这里不太理解
这里表述的有点问题,应该想表达的意思是如果当前匹配失败了,后面即使能凑成target,后续搜索也会失败。这是因为,我们每次选择的时候,尽可能的选择长度比较长的火柴肯定会得到较优解。就和找零钱一样,假设我们手上有5块和10块的钱,别人买票用的20,10块钱,一张票5元。别人拿20来买一张票,我肯定找10+5而不是5+5+5(这也是Leetcode一到贪心的题目,具体那一道题我忘了。)
嗯嗯,谢谢回复呀,还有个小问题就是,该题目这种模式下,解集是唯一的吗? 这么说应该不是唯一吧,
是不唯一的,比如4个4和8个2,组合方式就有多种。但是按照上述贪心的思路,如果找不到可行解就一定没有解。
Leetcode860
xiexie ^^
我刚刚写完code, 发现如果把 if (SubSum[i]+nums[index]> len) continue; 改成 if (SubSum[i]> len) continue;
就会出现running time error, 这是为什么啊?Last executed input:
[1,1,2,2,2] 按理来说,这样改了只是剪枝剪得晚一点哇, 为什么会有error呢?
后来发现这种情况是不可能的,因为一开始算边长的时候,就是用所有火柴棍的边长总和除以4的,所以如果不用完所有火柴棍,是无法获得满足条件的四条边长的。所以,如果这个正方形存在的话,递归深度一定会有n层,n是火柴棍总数。如果这个正方形不存在的话,一定会有边长超过len, 一旦超过,我们就没有必要继续往下走。这也算是一种剪枝操作了,就是我们没有必要把所有的情况都完全枚举出来,再判断4条边是不是符合条件,而是在枚举的过程中,如果发现某一条边不符合情况了,就不再往下枚举,这样就可以一下子否定掉一大堆不符合条件的。
这是我的思考过程,记录在这里,共勉。不对的地方希望大家指正,谢谢了。
https://www.acwing.com/video/129/看这个视频
为什么排序了之后就会比较快一些呢?
优先搜索,数据越大,进行搜素的选择就越少,降低搜索的复杂度
注:我的方法是先找到几个数能凑成一条边, mark一下这几个数保证下一层dfs不能用了,继续dfs直到找到4条边
看到你在问答区的帖子了,已解答。帖子链接点这里.
时间复杂度求教大大:
我自己写的dfs没有用四个target,而是只用了一个target,python400ms左右ac。然后看到题主的方法觉得更简洁, 用python照搬(代码如下)结果TLE, 过不了倒数第二个testcase。
问题:假如我的python照搬没错,如何解释这个时间差异?在我看来,栈深跟剪枝情况都应该是一样的。
照搬的code:
我的code:
我用你的“照搬的code”提交是能过的,你要不再提交试试
问题已solved。thx!