Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match girl has, please find out a way you can make one square by using up all those matchsticks. You should not break any stick, but you can link them up, and each matchstick must be used exactly one time.

Your input will be several matchsticks the girl has, represented with their stick length. Your output will either be true or false, to represent whether you could make one square using all the matchsticks the little match girl has.

Example 1:

Input: [1,1,2,2,2]
Output: true Explanation: You can form a square with length 2, one side of the square came two sticks with length 1.

Example 2:

Input: [3,3,3,3,4]
Output: false Explanation: You cannot find a way to form a square with all the matchsticks.

Note:

  1. The length sum of the given matchsticks is in the range of 0 to 10^9.
  2. The length of the given matchstick array will not exceed 15.

我已经服了LeetCode了,连卖火柴的小女孩也能改编成题目,还能不能愉快的玩耍了,坐等灰姑娘,丑小鸭的改编题了。好了,言归正传,这道题让我们用数组中的数字来摆出一个正方形。跟之前有道题Partition Equal Subset Sum有点像,那道题问我们能不能将一个数组分成和相等的两个子数组,而这道题实际上是让我们将一个数组分成四个和相等的子数组。我一开始尝试着用那题的解法来做,首先来判断数组之和是否是4的倍数,然后还是找能否分成和相等的两个子数组,但是在遍历的时候加上判断如果数组中某一个数字大于一条边的长度时返回false。最后我们同时检查dp数组中一条边长度位置上的值跟两倍多一条边长度位置上的值是否为true,这种方法不幸TLE了。所以只能上论坛求助各路大神了,发现了可以用优化过的递归来解,递归的方法基本上等于brute force,但是C++版本的直接递归没法通过OJ,而是要先给数组从大到小的顺序排序,这样大的数字先加,如果超过target了,就直接跳过了后面的再次调用递归的操作,效率会提高不少,所以会通过OJ。下面来看代码,我们建立一个长度为4的数组sums来保存每个边的长度和,我们希望每条边都等于target,数组总和的四分之一。然后我们遍历sums中的每条边,我们判断如果加上数组中的当前数字大于target,那么我们跳过,如果没有,我们就加上这个数字,然后对数组中下一个位置调用递归,如果返回为真,我们返回true,否则我们再从sums中对应位置将这个数字减去继续循环,参见代码如下:

解法一:

class Solution {
public:
bool makesquare(vector<int>& nums) {
if (nums.empty() || nums.size() < ) return false;
int sum = accumulate(nums.begin(), nums.end(), );
if (sum % != ) return false;
vector<int> sums(, );
sort(nums.rbegin(), nums.rend());
return helper(nums, sums, , sum / );
}
bool helper(vector<int>& nums, vector<int>& sums, int pos, int target) {
if (pos >= nums.size()) {
return sums[] == target && sums[] == target && sums[] == target;
}
for (int i = ; i < ; ++i) {
if (sums[i] + nums[pos] > target) continue;
sums[i] += nums[pos];
if (helper(nums, sums, pos + , target)) return true;
sums[i] -= nums[pos];
}
return false;
}
};

其实这题还有迭代的方法,很巧妙的利用到了位操作的特性,前面的基本求和跟判断还是一样,然后建立一个变量all,初始化为(1 << n) - 1,这是什么意思呢,all其实是一个mask,数组中有多少个数字,all就有多少个1,表示全选所有的数字,然后变量target表示一条边的长度。我们建立两个一位向量masks和validHalf,其中masks保存和target相等的几个数字位置的mask,validHalf保存某个mask是否是总和的一半。然后我们从0遍历到all,实际上就是遍历所有子数组,然后我们根据mask来计算出子数组的和,注意这里用了15,而不是32,因为题目中说了数组元素个数不会超过15个。我们算出的子数组之和如果等于一条边的长度target,我们遍历masks数组中其他等于target的子数组,如果两个mask相与不为0,说明有公用的数字,直接跳过;否则将两个mask或起来,说明我们当前选的数字之和为数组总和的一半,更新validHalf的对应位置,然后我们通过all取出所有剩下的数组,并在validHalf里查找,如果也为true,说明我们成功的找到了四条边,参见代码如下:

解法二:

class Solution {
public:
bool makesquare(vector<int>& nums) {
if (nums.empty() || nums.size() < ) return false;
int sum = accumulate(nums.begin(), nums.end(), );
if (sum % != ) return false;
int n = nums.size(), all = ( << n) - , target = sum / ;
vector<int> masks, validHalf( << n, false);
for (int i = ; i <= all; ++i) {
int curSum = ;
for (int j = ; j <= ; ++j) {
if ((i >> j) & ) curSum += nums[j];
}
if (curSum == target) {
for (int mask : masks) {
if ((mask & i) != ) continue;
int half = mask | i;
validHalf[half] = true;
if (validHalf[all ^ half]) return true;
}
masks.push_back(i);
}
}
return false;
}
};

类似题目:

Partition Equal Subset Sum

参考资料:

https://discuss.leetcode.com/topic/72107/java-dfs-solution-with-explanation

https://discuss.leetcode.com/topic/72232/c-bit-masking-dp-solution-with-detailed-comments

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Matchsticks to Square 火柴棍组成正方形的更多相关文章

  1. 473 Matchsticks to Square 火柴拼正方形

    还记得童话<卖火柴的小女孩>吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法.不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到.输入为小女孩拥有火柴的 ...

  2. Leetcode: Matchsticks to Square && Grammar: reverse an primative array

    Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match ...

  3. Leetcode之深度优先搜索(DFS)专题-473. 火柴拼正方形(Matchsticks to Square)

    Leetcode之深度优先搜索(DFS)专题-473. 火柴拼正方形(Matchsticks to Square) 深度优先搜索的解题详细介绍,点击 还记得童话<卖火柴的小女孩>吗?现在, ...

  4. [Swift]LeetCode473. 火柴拼正方形 | Matchsticks to Square

    Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match ...

  5. [Swust OJ 179]--火柴棍(找规律)

    题目链接:http://acm.swust.edu.cn/problem/0179/ Time limit(ms): 1000 Memory limit(kb): 65535   Descriptio ...

  6. BZOJ3324 : [Scoi2013]火柴棍数字

    为了使数字最大,首先要最大化其位数. 设$f[i][j][k]$表示从低到高考虑了$i$位,手头火柴棍个数为$j$,第$i$位是不是$0$时,最少移动多少根火柴. 若$f[i][0][非0]\leq ...

  7. 【LeetCode】473. Matchsticks to Square 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 回溯法 日期 题目地址:https://leetco ...

  8. LeetCode OJ 之 Maximal Square (最大的正方形)

    题目: Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and ...

  9. [LeetCode] 221. Maximal Square 最大正方形

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and ret ...

随机推荐

  1. 探寻 JavaScript 逻辑运算符(与、或)的真谛

    十二月已经过半,冬季是一个美妙的季节,寒冷的空气逼得人们不得不躲在安逸舒适的环境里生活.冬季会给人一种安静祥和的氛围,让人沉浸在其中,仿佛是一个旧的阶段的结束,同时也是一个新的阶段的开始.这么说来,西 ...

  2. 没有神话,聊聊decimal的“障眼法”

    0x00 前言 在上一篇文章<妥协与取舍,解构C#中的小数运算>的留言区域有很多朋友都不约而同的说道了C#中的decimal类型.事实上之前的那篇文章的立意主要在于聊聊使用二进制的计算机是 ...

  3. UML类图几种关系的总结

    在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregation),组合(Composit ...

  4. LINQ to SQL语句(19)之ADO.NET与LINQ to SQL

    它基于由 ADO.NET 提供程序模型提供的服务.因此,我们可以将 LINQ to SQL 代码与现有的 ADO.Net 应用程序混合在一起,将当前 ADO.NET 解决方案迁移到 LINQ to S ...

  5. Windows进程间通信—命名管道

    命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节.我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信.与Socket网络通信相比,命名管道不再需要编写身份验证的代码.将 ...

  6. Eclipse Meaven Spring SpringMVC Mybaits整合

    本示例是在:Ubuntu15上实现的:Windows上安装Maven将不太相同. Maven Install Run command sudo apt-get install maven, to in ...

  7. Hibernate(二)__简单实例入门

    首先我们进一步理解什么是对象关系映射模型? 它将对数据库中数据的处理转化为对对象的处理.如下图所示: 入门简单实例: hiberante 可以用在 j2se 项目,也可以用在 j2ee (web项目中 ...

  8. SQL Server游标(转)

    清晰地介绍了SQL游标,很好的学习资料. 转自 http://www.cnblogs.com/knowledgesea/p/3699851.html 什么是游标 结果集,结果集就是select查询之后 ...

  9. 如何判断一个DOM元素正在动画,一个CSS“阻塞”JS的例子

    一般情况下CSS不会直接影响JS的程序逻辑,但是以CSS实现动画的话,这个便不太确定了,这个故事发生在与UED迁移全局样式的过程. 曾经我有一段实现弹出层隐藏动画的代码是这个样子的: if (this ...

  10. Centos7更改默认启动模式(转载)

    今天心血来潮安装一个centos7的图形界面,但发现用之前的方式无法修改默认启动为命令行模式. 之前的方法:修改/etc/inittab文件中的           id:3:initdefault ...