开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 21 天,点击查看活动详情
你将得到一个整数数组 matchsticks ,其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。
如果你能使这个正方形,则返回 true ,否则返回 false 。
示例 1:
输入: matchsticks = [1,1,2,2,2] 输出: true 解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: matchsticks = [3,3,3,3,4] 输出: false 解释: 不能用所有火柴拼成一个正方形。
提示:
1 <= matchsticks.length <= 15
1 <= matchsticks[i] <= 108
思路
首先对火柴棍求和,得到边长 sideside ,若总长不能被 44 整除则返回 falsefalse。
1. 状态压缩:
最多有 1515 根火柴棍,考虑用一个整型 statestate 表示每根火柴棍是否被选择的状态,statestate 第 ii 位为 11 表示 matchsticks[i]matchsticks[i] 已选择。
2. 搜索策略:
dfs 函数的含义是:查询从当前状态出发,能否在每一步都满足条件的情况下,将所有火柴棍都选择。
具体而言,statestate 表示当前状态,sumsum 表示当前累加边长,其取值范围为 [0,side)[0,side)。每一步遍历挑选一个还未被选择的火柴 ii( statestate 相应位为 00 ),若满足条件 sum+matchsticks[i]<=sidesum+matchsticks[i]<=side, 说明可以选择,继续向下搜索。若搜索到终止状态 (1<<n)−1(1<<n)−1 ,提前返回。
3. 终止状态:
为所有火柴都能够被成功选择,此时返回 truetrue,因此设置 cache[(1<<n)−1]=1cache[(1<<n)−1]=1 。
代码
#define setBit(x, n) (x | (1 << n))
class Solution {
public:
int side; // 边长
vector<int8_t> cache;
bool makesquare(vector<int>& matchsticks) {
// 求出边长
int sum = accumulate(matchsticks.begin(), matchsticks.end(), 0);
if(sum % 4) return false;
side = sum / 4;
// dfs
int n = matchsticks.size();
cache = vector<int8_t>((1 << n), -1);
cache[(1 << n) - 1] = 1;
return dfs(0, 0, matchsticks);
}
// state - 当前状态,第 i 位为 1 表示 matchsticks[i] 已选择
// sum - 表示当前累加边长
bool dfs(int state, int sum, vector<int>& matchsticks){
if(cache[state] != -1) return cache[state];
int n = matchsticks.size();
bool ans = 0;
for(int i=0; i<n; ++i){
if(getBit(state, i) == 0 && sum + matchsticks[i] <= side) {
int new_sum = ( sum + matchsticks[i] ) % side;
if( dfs(setBit(state, i), new_sum, matchsticks) ) {
ans = 1;
break;
};
}
}
cache[state] = ans;
return ans;
}
};