当前位置: 首页 > news >正文

[模版总结] - 集合划分类DFS模版

题目描述

给定一个数组,给定一个数字k, 问能不能讲数组内数等分成k份,使得每一个集合中数和相等。

题目链接

下面两道题问题及其类似,可作为同一类题目思考

Leetcode 698

Leetcode 473

题目思路

这道题是一道经典集合划分类问题,这类问题问询通常是判读是否将一个集合里面的所有元素划分到不同满足一定条件的子集合中。

集合划分也是思路和排列组合类似,可以想象成给球分桶的问题,这种类型的题目通常有两个思考方式:给每一个球选桶,给每一个桶选球

1. 给每一个球选桶:我们每一层递归的是球,每一个球每层递归可以选择进入一个桶,以此类推。

2. 给每一个桶选球:我们每一层递归的是桶,相当于每一层给某一个桶塞球,当塞满了就开始进入下一个桶。

这里的球就是指集合中的每一个数,而桶则是指等分的每一个子集合。

1. 暴力DFS

给每一个数选择一个子集合(超时)- 思路很简单,每一层递归是给当前数字选择一个子集合,如果当前子集合超过等分值那么跳过,直到所有数字选完子集合后,比较子集合中是否相等即可。

代码如下:

class Solution {int[] arr;int avg;public boolean canPartitionKSubsets(int[] nums, int k) {int sum = 0;for (int i: nums) sum+=i;avg = sum/k;if (avg*k!=sum) return false;arr = nums;return dfs(0, new int[k]);}private boolean dfs(int idx, int[] bkts) {if (idx==arr.length) {// 所有球找完桶,这时确定每个桶里面的数字for (int i: bkts) {if (i!=avg) return false;}return true;}// 开始选桶for (int i=0; i<bkts.length; i++) {if (bkts[i]+arr[idx] > avg) continue;// 桶里还能加bkts[i]+=arr[idx];if (dfs(idx+1, bkts)) return true;bkts[i]-=arr[idx]; // 组合类题目要回溯}return false;}
}

时间复杂度:O(k^N), 每一个数字有k个选择方式;空间复杂度:O(1),如果忽略递归占用的额外占空间。

给每一个子集合加入数(通过) - 相当于给每一个子集合选择一套数字,如果这个子集合塞满了,则继续下一个子集合直到所有的“桶”都塞完了“球”。思路和球选桶类似,但是需要开额外空间加入标记位来确认当前球被选过没有。

代码如下:

class Solution {int[] arr;int avg;public boolean canPartitionKSubsets(int[] nums, int k) {int sum = 0;for (int i: nums) sum+=i;avg = sum/k;if (avg*k!=sum) return false;arr = nums;return dfs(0, 0, new int[k], new boolean[nums.length]);}private boolean dfs(int idx, int bkt, int[] bkts, boolean[] visited) {if (bkt==bkts.length) {for (int i: bkts) {if (i!=avg) return false;}return true;}if (bkts[bkt]==avg) {// 目前选满了,换下一个桶return dfs(0, bkt+1, bkts, visited);}// 当前桶塞球for (int i=idx; i<arr.length; i++) {if (visited[i]) continue; //球用过了if (bkts[bkt]+arr[i]>avg) continue;// 当前桶可以塞visited[i] = true;bkts[bkt]+=arr[i];if (dfs(i+1, bkt, bkts, visited)) return true;visited[i] = false;bkts[bkt]-=arr[i]; // 组合问题回溯}return false;}
}

时间复杂度:O(k\times 2^N), 比球选桶的思路快了不少, 每一个桶在选球时有2^N个选法;空间复杂度:O(N),需要开额外空间进行去重。

2. DFS + 剪枝优化

给每一个数选择一个子集合(通过) - 不难理解,在递归寻找解的过程中,有很多重复解的情况,比如桶1选择2,3,4号球,桶2选择5,6号球,这种情况和桶1选择5,6号球,桶2选择2,3,4号球是重复情况。我们只是需要知道组合数而不需要知道排序。

代码如下:

class Solution {int[] arr;int avg;public boolean canPartitionKSubsets(int[] nums, int k) {int sum = 0;for (int i: nums) sum+=i;avg = sum/k;if (avg*k!=sum) return false;arr = nums;return dfs(0, new int[k]);}private boolean dfs(int idx, int[] bkts) {if (idx==arr.length) {// 所有球找完桶,这时确定每个桶里面的数字for (int i: bkts) {if (i!=avg) return false;}return true;}// 开始选桶for (int i=0; i<bkts.length; i++) {if (bkts[i]+arr[idx] > avg) continue;// 相邻两个桶值相同,那么选了上一个就不需要选这个if (i>0 && bkts[i-1]==bkts[i]) continue;// 桶里还能加bkts[i]+=arr[idx];if (dfs(idx+1, bkts)) return true;bkts[i]-=arr[idx]; // 组合类题目要回溯}return false;}
}

时间复杂度:最坏O(k^N), 具体复杂度很难估计;空间复杂度:O(1),如果忽略递归占用的额外占空间。

给每一个子集合加入数(通过) - 给桶塞球的思路我们可能会给两个桶塞入相同组合的球,那么可以设置一个HashMap记录之前的球选择,如果球组合被选过那么直接返回结果而不在往后计算。

注:这里还有一个优化是对boolean[] visited数组进行空间优化,我们可以直接用一个N位 int 来保存访问信息,但你需要熟悉下面几个位操作 - 非常巧妙

1. ((int >> i) & 1) == 1: 判断第i个球是否用过 等价于 visited[i]==true

2. int |= 1<<i: 给第i个球标记为访问 等价于 visited[i] = true

3. int ^= 1<<i: 给第i个球回溯为未访问 等价于 visited[i] = false

代码如下:

class Solution {int[] arr;int limit;HashMap<Integer, Boolean> paths = new HashMap<>();public boolean canPartitionKSubsets(int[] nums, int k) {int sum = 0;for (int i: nums) sum+=i;boolean[] visited = new boolean[nums.length];int avg = sum/k;if (avg*k!=sum) return false;arr = nums;limit = avg;// 骚套路剪枝通过int来记录每一个元素使用与否int used = 0;return dfs(used, 0, new int[k], 0);}private boolean dfs(int used, int bkt, int[] bkts, int start) {// 以k个桶的角度选择候选if (bkt==bkts.length) return true;if (bkts[bkt]==limit) {boolean res =  dfs(used, bkt+1, bkts, 0);paths.put(used, res);return res;} if (paths.containsKey(used)) {return paths.get(used);}for (int i=start; i<arr.length; i++) {if (((used>>i) & 1)==1) continue;if (bkts[bkt]+arr[i]>limit) continue;used |= 1 << i;bkts[bkt]+=arr[i];if (dfs(used, bkt, bkts, start+1)) return true;bkts[bkt]-=arr[i];used ^= 1 << i;}return false;}
}

时间复杂度:最坏O(k\times 2^N), 比球选桶的思路快了不少, 每一个桶在选球时有2^N个选法;空间复杂度:O(N),虽然省去了visited的空间,但是需要开额外空间进行路径去重。

相关文章:

[模版总结] - 集合划分类DFS模版

题目描述 给定一个数组&#xff0c;给定一个数字k, 问能不能讲数组内数等分成k份&#xff0c;使得每一个集合中数和相等。 题目链接 下面两道题问题及其类似&#xff0c;可作为同一类题目思考 Leetcode 698 Leetcode 473 题目思路 这道题是一道经典集合划分类问题&#…...

JavaScript中复制新的数组与原数组删除某个值——不影响新复制的数组的方法详解

系列文章目录 文章目录 系列文章目录前言一、使用slice()方法复制数组二、使用concat()方法复制数组三、使用扩展运算符(...)复制数组总结 前言 在JavaScript中&#xff0c;我们经常需要处理数组的复制和修改。本文将详细介绍如何在JavaScript中复制一个新的数组&#xff0c;并…...

easyui主表子表维护页面

easyui主表子表维护页面 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>Title</title><!-- <#include "common.html"/> --><link rel"stylesheet" type&quo…...

k8s exam

Pause 容器是 Pod 中的第一个启动的容器&#xff0c;其他所有的用户容器都是其子进程当 Pod 被从节点中删除时&#xff0c;与之关联的 emptyDir 中的数据也将被永久删除。持久存储用PV&#xff0c;PVCService 资源定义了如何访问应用&#xff0c;但实际的网络流量管理和路由是由…...

C#,中国福利彩票《刮刮乐》的数学算法(02)——时来运转

1 中国福利彩票 中国福利彩票始于1987年7月27日&#xff0c;以“团结各界热心社会福利事业的人士&#xff0c;发扬社会主义人道主义精神&#xff0c;筹集社会福利资金&#xff0c;兴办残疾人、老年人、孤儿福利事业和帮助有困难的人”、即“扶老、助残、救孤、济困”为宗旨。随…...

我的观影记录表【个人向】

目录 前言电影评分标准闪电侠&#xff08;2023&#xff09;银河护卫队3&#xff08;2023&#xff09; 前言 这里是我本人的观影记录&#xff0c;这个想法2年前就有了&#xff0c;但是一直比较懒&#xff0c;现在&#xff08;上班摸鱼&#xff09;准备重新开始&#xff0c;评价…...

网络安全策略应包含哪些?

网络安全策略是保护组织免受网络威胁的关键措施。良好的网络安全策略可以确保数据和系统的保密性、完整性和可用性。以下是一个典型的网络安全策略应包含的几个重要方面&#xff1a; 1. 强化密码策略&#xff1a;采用强密码&#xff0c;要求定期更换密码&#xff0c;并使用多因…...

【Git】Git GitHub

1. Git1.1 Git基本操作1.2 Git版本回退1.3 Git分支操作 2. Git 配合GitHub2.1 生成密钥2.2 GitHub添加公钥2.3 Git连接GitHub2.4 本地仓库关联远程仓库2.5 本地代码push远程仓库2.6 本地clone远程仓库2.7 本地fetch和pull 1. Git 1.1 Git基本操作 touch test.py 工作区创建文…...

[STL]详解list模拟实现

[STL]list模拟实现 文章目录 [STL]list模拟实现1. 整体结构总览2. 成员变量解析3. 默认成员函数构造函数1迭代器区间构造函数拷贝构造函数赋值运算符重载析构函数 4. 迭代器及相关函数迭代器整体结构总览迭代器的模拟实现begin函数和end函数begin函数和end函数const版本 5. 数据…...

C和C++的区别与联系

C语言&#xff08;C&#xff09;和C语言&#xff08;C&#xff09;是两种编程语言&#xff0c;它们之间有许多区别和联系。以下是它们之间的主要区别和联系&#xff1a; 区别&#xff1a; 历史和起源&#xff1a; C语言是由Dennis Ritchie于20世纪70年代初在贝尔实验室开发的。…...

springboot通过接口执行本地shell脚本

首先创建springboot项目 shell脚本 #!/bin/shecho Hello World&#xff01;然后编写执行shell脚本的util类 import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List;pub…...

工欲善其事必先利其器,IT工作电脑更要维护好

目录 一&#xff1a;电脑的组成 二&#xff1a;维护措施 三&#xff1a;助力记忆 一&#xff1a;电脑的组成 当谈到电脑主机时&#xff0c;我们通常指的是电脑的中央处理器(CPU)、内存、主板、电源、硬盘、显卡、声卡、网卡等核心部件组成的整体。这些部件共同协作&#xff…...

移动端个人中心UI设计

效果图 源码如下 页面设计 <template><div class"container"><!-- 顶部用户信息 start--><div class"header"><div class"user-info"><van-image class"user-img" round width"70" :sr…...

开发接口,你需要先搞懂这些概念!

SOA Service Oriented Ambiguity 即面向服务架构&#xff0c; 简称SOA。 SOA的提出是在企业计算领域&#xff0c;就是要将紧耦合的系统&#xff0c;划分为面向业务的&#xff0c;粗粒度&#xff0c;松耦合&#xff0c;无状态的服务。服务发布出来供其他服务调用&#xff0c;一…...

zookeeper常用命令

zkClient 简介 zkClient是简易的客户端程序 进入zkClient 在bin目录下输入zkCli.sh 节点命令 增 create 路径 数据 -s&#xff1a;顺序节点 -e&#xff1a;临时节点 默认情况下&#xff0c;不添加-s或者-e参数的&#xff0c;创建的是持久节点改 set 路径 数据 版本…...

亚马逊水基灭火器UL8测试报告ISO17025实验室办理

在跨境电商平台上销售的境外电商&#xff0c;在美国市场中需要提供相关的安全规范报告。其中&#xff0c;美国相关部门要求&#xff0c;如果商家未能提交UL&#xff08;Underwriters Laboratories&#xff09;标准的检测报告&#xff0c;将会被责令停止销售。而为了在亚马逊、T…...

Shell学习脚本-if多分支结构

语法&#xff1a; if 条件then指令集 else指令集 fi特殊写法&#xff1a; if [ -f "$file1" ]; then echo 1; else echo 0; fi 相当于&#xff1a; [ -f "$file1" ] && echo 1 || echo 0 多分支结构&#xff1a; if 条件then指令 elif 条件th…...

[SQL挖掘机] - 窗口函数 - lead

介绍: lead() 是一种常用的窗口函数&#xff0c;它用于获取某一行之后的行的值。它可以用来在结果集中的当前行后面访问指定列的值。 用法: lead() 函数的语法如下&#xff1a; lead(列名, 偏移量, 默认值) over (partition by 列名1, 列名2, ... order by 列名 [asc|desc]…...

PyTorch Lightning教程四:超参数的使用

如果需要和命令行接口进行交互&#xff0c;可以使用Python中的argparse包&#xff0c;快捷方便&#xff0c;对于Lightning而言&#xff0c;可以利用它&#xff0c;在命令行窗口中&#xff0c;直接配置超参数等操作&#xff0c;但也可以使用LightningCLI的方法&#xff0c;更加轻…...

2023 蓝桥杯真题B组 C/C++

https://www.dotcpp.com/oj/train/1089/ 题目 3150: 蓝桥杯2023年第十四届省赛真题-冶炼金属 题目描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V&#xff0c;V 是一个正整数&#xff0c;这意味着消耗 V 个普通金 属 O…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...