【leetcode】473. 火柴拼正方形(dfs深度优先搜索)

题目

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

示例 1:

输入: [1,1,2,2,2]
输出: true

解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:

输入: [3,3,3,3,4]
输出: false

解释: 不能用所有火柴拼成一个正方形。
注意:

  • 给定的火柴长度和在 0 到 10^9之间。
  • 火柴数组的长度不超过15。

分析

因此,对于给定的若干根火柴,我们需要:

  • 将它们分成四组,每一根火柴恰好属于其中的一组;
  • 每一组火柴的长度之和都相同,等于所有火柴长度之和的四分之一。

dfs 深度优先搜索

我们可以使用深度优先搜索枚举出所有的分组情况,并对于每一种情况,判断是否满足上述的两个条件。

我们依次对每一根火柴进行搜索,当搜索到第 i 根火柴时,我们可以把它放到四组中的任意一种。对于每一种放置方法,我们继续对第 i + 1 根火柴进行递归搜索。当我们搜索完全部的 N 根火柴后,再判断每一组火柴的长度之和是否都相同。

dfs要注意剪枝细节,否则会超时:
在进行搜索之前,我们可以将火柴的长度从大到小进行排序,方便我们先搜索较长的火柴。

例如当火柴的长度为 [4,4,4,8] 时,每条边的长度为 5,如果我们先搜索长度为 8 的火柴,就可以发现它无法被放在任意一组中,因此直接退出搜索返回 False。

  • 时间复杂度:O(4^N ),其中 N 是火柴的数量。
  • 空间复杂度:O(N)。
class Solution {
public:
    int target; // 目标边长
    int bian[4]; 
    // vector<int> flag; 
    bool makesquare(vector<int>& nums) {
        if(nums.size()<=3) 
            return false; //剪枝1
        int length = 0;
        for(int i=0; i<nums.size(); i++){
            length += nums[i];
        }
        if( length%4 )  //剪枝2
            return false;
        target = length/4; 
        //排序细节
        // sort(nums.begin(), nums.end()); //升序排序会超时!!
        sort(nums.rbegin(), nums.rend()); //降序排序(反向迭代器)
        for(int i=0;i<4;i++)    bian[i] = 0;
        return dfs(nums,0);
    }
    bool dfs(vector<int>& nums, int index){
        if( index == nums.size() ){
            return target==bian[0] && target==bian[1] && target==bian[2] && target==bian[3];
        }
        int val = nums[index];
        if(val > target)
            return false; //剪枝3
        //在四条边中找位置
        for(int i=0; i<4; i++){
            if( (bian[i]+val) > target )
                continue;
            bian[i] += val; //执行操作
            //回溯 注意这里不是直接返回啊!!!如果false还要去还原操作再次尝试的
            // return dfs(nums, index+1);
            if( dfs(nums, index+1) )
                return true;
            bian[i] -= val; //还原操作
        }
        return false;
    }

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值