「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
题目
你将得到一个整数数组 matchsticks ,其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。
如果你能使这个正方形,则返回 true ,否则返回 false 。
示例 1:
输入:matchsticks = [1,1,2,2,2]
输出:true
解释:能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入:matchsticks = [3,3,3,3,4]
输出:false
解释:不能用所有火柴拼成一个正方形。
思路
题意不难理解。因为题目中不能折断火柴棒,所以我们可以先求出火柴棒总和sum,如果sum不能被4整除,肯定就不能拼成正方形。在可以被4整除的情况下,我们用sum/4得到正方形的边长standard,题目就转化为了,能否把火柴棒分成4组,使得每组的和都是standard。
我们可以考虑搜索回溯的方法,记正方形的当前4条边长为len[0]~len[3]。每一根火柴棒都可以选择加入4条边的任意一条,等所有的火柴棒都放完之后,我们校验4条边长是否都等于standard就好。当然,在火柴棒放完的前提下,只要校验前3条变成是否等于standard就好,因为前3条都等于standard的话,最后1条也一定等于。
上面的方法复杂度较高,我们接下来思考如何剪枝:
- 当前火柴棒放入这条边,如果长度超过standard
- 当前火柴棒放入len[i]之前,i>0 且 len[i-1]==len[i] 上面2种情况都可以剪枝,情况1很好理解,我们来看情况2。因为遍历的时候从前往后,所以既然遍历到情况2,那么当前火柴棒放入len[i-1]这条边肯定是搜索过的结果False,而如果len[i-1]==len[i],那么如果我们把len[i]看作是len[i-1],结果肯定是一样的,所以这种情况也可以被剪掉。
Java版本代码
class Solution {
public boolean makesquare(int[] matchsticks) {
int sum = Arrays.stream(matchsticks).sum();
if (sum % 4 != 0) {
return false;
}
return dfs473(matchsticks, 0, new int[4], sum >> 2);
}
private static boolean dfs473(int[] matchsticks, int index, int[] len, int standard) {
if (index == matchsticks.length) {
if (len[0] == standard && len[1] == standard && len[2] == standard) {
return true;
}
return false;
}
int current = matchsticks[index];
for (int i = 0; i < 4; i++) {
if ((len[i] + current > standard) || (i > 0 && len[i-1] == len[i])) {
continue;
}
len[i] += current;
if (dfs473(matchsticks, index+1, len, standard)) {
return true;
}
len[i] -= current;
}
return false;
}
}