【动态规划】【记忆化搜索】【状态压缩】1681. 最小不兼容性
作者推荐
【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字
本文涉及知识点
动态规划汇总
状态压缩 记忆化搜索
1681. 最小不兼容性
给你一个整数数组 nums 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中,使得同一个子集里面没有两个相同的元素。
一个子集的 不兼容性 是该子集里面最大值和最小值的差。
请你返回将数组分成 k 个子集后,各子集 不兼容性 的 和 的 最小值 ,如果无法分成分成 k 个子集,返回 -1 。
子集的定义是数组中一些数字的集合,对数字顺序没有要求。
示例 1:
输入:nums = [1,2,1,4], k = 2
输出:4
解释:最优的分配是 [1,2] 和 [1,4] 。
不兼容性和为 (2-1) + (4-1) = 4 。
注意到 [1,1] 和 [2,4] 可以得到更小的和,但是第一个集合有 2 个相同的元素,所以不可行。
示例 2:
输入:nums = [6,3,8,1,3,1,2,2], k = 4
输出:6
解释:最优的子集分配为 [1,2],[2,3],[6,8] 和 [1,3] 。
不兼容性和为 (2-1) + (3-2) + (8-6) + (3-1) = 6 。
示例 3:
输入:nums = [5,3,3,6,3,3], k = 3
输出:-1
解释:没办法将这些数字分配到 3 个子集且满足每个子集里没有相同数字。
提示:
1 <= k <= nums.length <= 16
nums.length 能被 k 整除。
1 <= nums[i] <= nums.length
动态规划
对nums按升序排序。
动态规划的状态表示
pre[mask][end] 记录最小不兼容性和。mask表示nums中那些元素已经选择,选择的数优先放组号小的组。1组满了后,才放2组;2组满了,才放三组 ⋯ \cdots ⋯
动态规划的转移方程
mask1 = mask | (1 << j )
end1 = j
j必须符合以下条件:
- j未被使用。
- 如果是某个组的首元素,可以选择任意元素。
- 如果不是某个组的首元素,j > end。且nums[j] != nums[end]
{ d p [ m a s k 1 ] [ j ] = d p [ m a s k ] [ e n d ] 某组的首元素 d p [ m a s k 1 ] [ j ] = d p [ m a s k ] [ e n d ] + n u m s [ j ] − n u m s [ e n d ] 非组首元素 \begin{cases} dp[mask1][j]= dp[mask][end] & 某组的首元素\\ dp[mask1][j]= dp[mask][end] + nums[j]-nums[end] & 非组首元素 \end{cases} {dp[mask1][j]=dp[mask][end]dp[mask1][j]=dp[mask][end]+nums[j]−nums[end]某组的首元素非组首元素
动态规划的初始值
dp[0][0]全部为0,其它全部为10000。
动态规划的填表顺序
mask从小到大,枚举前置条件。
动态规划的返回值
dp.back()的最小值。
代码
核心代码
class Solution {
public:int minimumIncompatibility(vector<int>& nums, int k) {m_c = nums.size();m_iMaskCount = 1 << m_c;sort(nums.begin(), nums.end());vector<int> vBitCount(m_iMaskCount);for (int i = 1; i < m_iMaskCount; i++){vBitCount[i] = 1 + vBitCount[i & (i - 1)];}vector<vector<int>> dp(m_iMaskCount, vector<int>(m_c, m_iNotMay));dp[0][0] = 0;for (int mask = 0; mask < m_iMaskCount; mask++){bool bGroupFirst = (0 == vBitCount[mask] % (m_c / k));for (int end = 0; end < m_c; end++){for (int j = bGroupFirst ? 0 : (end + 1); j < m_c; j++){if ((1 << j) & mask){continue;//已经选择}if ((nums[j] == nums[end])&& (!bGroupFirst)){continue;//相同} const int iNew = dp[mask][end] + (bGroupFirst ? 0 : (nums[j]-nums[end]));dp[mask | (1 << j)][j] = min(dp[mask | (1 << j)][j],iNew);}}}const int iRet = *std::min_element(dp.back().begin(), dp.back().end());return (iRet >= m_iNotMay) ? -1 : iRet;}int m_c, m_iMaskCount,m_iNotMay=10000;
};
测试用例
template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{ vector<int> nums;int k;{Solution sln;nums = { 1 }, k = 1;auto res = sln.minimumIncompatibility(nums, k);Assert(res, 0);}{Solution sln;nums = { 1,1 }, k = 1;auto res = sln.minimumIncompatibility(nums, k);Assert(res, -1);}{Solution sln;nums = { 1, 2, 1, 4 }, k = 2;auto res = sln.minimumIncompatibility(nums, k);Assert(res, 4);}{Solution sln;nums = { 6, 3, 8, 1, 3, 1, 2, 2 }, k = 4;auto res = sln.minimumIncompatibility(nums, k);Assert(res, 6);}{Solution sln;nums = { 5,3,3,6,3,3 }, k = 3;auto res = sln.minimumIncompatibility(nums, k);Assert(res, -1);}{Solution sln;nums = { 11,11,3,4,2,16,14,13,6,14,2,5,10,13,5,7 }, k = 8;auto res = sln.minimumIncompatibility(nums, k);Assert(res, 12);}
}
记忆化搜索+动态规划
从后置条件倒推前置条件,可以省去大量不必要的状态。运行速度提高500%。缺点可理解性大幅降低。
mask 选择了那些数,end 是最一个数。如果本组只有一个数,则最小不兼容性和就是 除本数外 的前几个完整的组的最小不兼容性和。
如果完整的组,最后一个元素一定是最大值。最大值一定是某个组的最后一个。将此组调到最后一组,结果不变。
EndZeroCount 从右到左为1的第一个下标(从0开始)。为了一致,nums降序排序。
每组一个元素要特殊处理。
int EndZeroCount(unsigned x )
{for (int i = 0; i < 32; i++){if ((1 << i) & x){return i;}}return 32;
}class Solution {
public:int minimumIncompatibility(vector<int>& nums, int k) {m_c = nums.size();m_iMaskCount = 1 << m_c;m_pre = m_c / k;if (1 == m_pre){return 0;} sort(nums.begin(), nums.end(),std::greater<>());m_nums = nums;m_vBitCount.resize(m_iMaskCount);for (int i = 1; i < m_iMaskCount; i++){m_vBitCount[i] = 1 + m_vBitCount[i & (i - 1)];}m_dp.assign(m_iMaskCount, vector<int>(m_c, m_iNotMay));const int iRet = Rec(m_iMaskCount-1);return (iRet >= m_iNotMay) ? -1 : iRet;}int Rec(int mask, int end){if (0 == mask){return 0;}auto& res = m_dp[mask][end];if (m_iNotMay != res){return res;}const int iPreMask = mask ^ (1 << end);const int cnt = m_vBitCount[mask] % m_pre;//最后一组数量if (1 == cnt ){return res = Rec(iPreMask);}for (int i = end+1 ; i < m_c; i++){if ((1 << i) & mask){if (m_nums[i] != m_nums[end]){res = min(res, Rec(iPreMask,i)+ m_nums[end]-m_nums[i]);}}}return res;}int Rec(int mask){return Rec(mask, EndZeroCount(mask));}int m_c, m_iMaskCount,m_iNotMay=10000, m_pre;vector<int> m_vBitCount;vector<vector<int>> m_dp;vector<int> m_nums;
};
2023年2月版
class Solution {
public:
int minimumIncompatibility(vector& nums, int k) {
m_c = nums.size();
if (k == m_c)
{
return 0;
}
if (1 == k)
{
std::set setNums(nums.begin(), nums.end());
if (nums.size() != setNums.size())
{
return -1;
}
return *setNums.rbegin() - *setNums.begin();
}
std::sort(nums.begin(),nums.end());
m_iMaskNum = (1 << m_c )*m_c;
m_vMaskByBits.resize(m_c + 1);
m_vMaskByBits[0].push_back(0);
vector vMaskBits(m_iMaskNum);
for (int mask = 1; mask < m_iMaskNum; mask++)
{
const int iSelMask = mask / m_c;
vMaskBits[mask] = vMaskBits[(iSelMask&(iSelMask - 1))m_c] + 1;
m_vMaskByBits[vMaskBits[mask]].push_back(mask);
}
m_vMaskGroupFirstToMin.resize(m_iMaskNum, m_iNotMay);
m_vMaskGroupFirstToMin[0] = 0;
for (int i = 0; i < nums.size(); i++)
{
vector dp(m_iMaskNum, m_iNotMay);
for (int iMask : m_vMaskByBits[i])
{
if (m_iNotMay == m_vMaskGroupFirstToMin[iMask])
{
continue;
}
const int iSelMask = iMask / m_c;
const int iPreSel = iMask% m_c;
if (0 == i % (m_c/k))
{//新组
for (int j = 0; j < m_c; j++)
{
if (iSelMask & (1 << j))
{
continue;
}
const int iNewMask = JoinMask(iSelMask | (1 << j), j);
dp[iNewMask] = min(dp[iNewMask], min(m_vMaskGroupFirstToMin[iMask],dp[iMask]));
}
}
else
{
for (int j = iPreSel+1; j < m_c; j++)
{
if (iSelMask & (1 << j))
{
continue;
}
const int iAdd = nums[j] - nums[iPreSel];
if (0 == iAdd)
{
continue;
}
const int iNewMask = JoinMask(iSelMask | (1 << j), j);
dp[iNewMask] = min(dp[iNewMask], min(m_vMaskGroupFirstToMin[iMask], dp[iMask]) + iAdd);
}
}
}
m_vMaskGroupFirstToMin.swap(dp);
}
std::set setRet;
for (int iPre = 0; iPre < m_c; iPre++)
{
int iIndex = (1 << m_c) - 1;
iIndex = iIndexm_c + iPre;
setRet.insert(m_vMaskGroupFirstToMin[iIndex]);
}
int iMin = setRet.begin();
return (m_iNotMay == iMin) ? -1 : iMin;
}
int JoinMask(int iSelMask, int iNewSelIndex)
{
return iSelMaskm_c + iNewSelIndex;
}
vector m_vMaskGroupFirstToMin;
int m_c;
int m_iMaskNum;
vector<vector> m_vMaskByBits;
const int m_iNotMay = 1000 * 1000;
};
2023年9月版
class Solution {
public:
int minimumIncompatibility(vector& nums, int k) {
m_c = nums.size();
if (k == m_c)
{
return 0;
}
m_iMaskNum = 1 << m_c;
if (0 != m_c % k)
{
return -1;
}
const int iNumOfAGroup = m_c / k;
vector<vector> vBitMask(m_c+1);
vBitMask[0].emplace_back(0);
for (int mask = 1; mask < m_iMaskNum; mask++)
{
vBitMask[bitcount((unsigned int)mask)].emplace_back(mask);
}
std::unordered_map<int, int> mMaskCom;
for (int mask : vBitMask[iNumOfAGroup])
{
int iMax = INT_MIN, iMin = INT_MAX;
unordered_set setValues;
for (int j = 0; j < m_c; j++)
{
if (mask & (1 << j))
{
MaxSelf(&iMax, nums[j]);
MinSelf(&iMin, nums[j]);
setValues.emplace(nums[j]);
}
}
if (setValues.size() != iNumOfAGroup)
{
continue;
}
mMaskCom[mask] = iMax - iMin;
}
int pre[1 << 16] = { 0 };
for (const auto& it : mMaskCom)
{
pre[it.first] = it.second;
}
for (int i = 2; i <= k; i++)
{
int dp[1 << 16] = { 0 };
for (const int& mask : vBitMask[iNumOfAGroup*i])
{
for (int sub = mask; sub; sub = (sub - 1) & mask)
{
if ((0 != pre[sub])&& mMaskCom.count(mask - sub))
{
int iNew = pre[sub] + mMaskCom[mask - sub];
if (0 != dp[mask])
{
iNew = min(iNew, dp[mask]);
}
dp[mask] = iNew;
}
}
}
memcpy(pre, dp, sizeof(dp));
}
return pre[m_iMaskNum - 1] ? pre[m_iMaskNum - 1] : -1;
}
int m_c, m_iMaskNum;
};

扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关
下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
| 我想对大家说的话 |
|---|
| 闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
| 如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

相关文章:
【动态规划】【记忆化搜索】【状态压缩】1681. 最小不兼容性
作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 本文涉及知识点 动态规划汇总 状态压缩 记忆化搜索 1681. 最小不兼容性 给你一个整数数组 nums 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中,使得同一…...
JVM-类加载器 双亲委派机制
申明:文章内容是本人学习极客时间课程所写,文字和图片基本来源于课程资料,在某些地方会插入一点自己的理解,未用于商业用途,侵删。 什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写&a…...
vue axios 请求后端无法传参问题
vue请求后端无法传参问题 问题描述处理过程总结 问题描述 在学习vue时,使用axios调用后端,发现无法把参数正确传到后端,现象如下: 使用vue发起请求,浏览器上已经有传参,但是后端没接收到对应的用户名密码&…...
打印最小公倍数
打印最小公倍数 题目描述: 输入2个整数m和n,计算m和n的最小公倍数,并打印出结果 测试1: 输入:18 24 输出:72 测试2: 输入:18 6 输出:18解法思路: 最小公倍数是指两个…...
[AIGC] Java 和 Kotlin 的区别
好的,我还是以“萌萌哒小码农”的身份继续回答您的问题。 Java 和 Kotlin 是两种不同的编程语言,它们有许多共同点,但也有一些重要的区别。以下是一些常见的 Java 和 Kotlin 的区别: 语法 Kotlin 的语法比 Java 简洁得多&#…...
蓝桥杯电子类单片机提升一——超声波测距
前言 单片机资源数据包_2023 一、超声波测距原理 二、超声波测距的应用 1.超声波的发射 2.单片机知识补充:定时器 3.超声波的接收与计时 4.距离的计算 1)定时器1为16位自动重载+1T11.0592MHz 2)定时器1为16位自动重载&am…...
前端架构: 脚手架开发流程中的难点梳理
脚手架的开发流程 1 )开发流程 创建 npm 项目创建脚手架入口文件,最上方添加: #!/usr/bin/env node 配置 package.json, 添加 bin 属性编写脚手架代码将脚手架发布到 npm 2 )使用流程 安装脚手架 npm install -g your-own-cli …...
django中配置使用websocket
Django 默认情况下并不支持 WebSocket,但你可以通过集成第三方库如 channels 来实现 WebSocket 功能。channels 是一个 Django 应用,它提供了对 WebSocket、HTTP2 和其他协议的支持。 下面是如何在 Django 项目中使用 WebSocket 的基本步骤:…...
Rust复合类型详解
在Rust中,复合类型是一种能够将多个值组合在一起的数据类型。本篇博客将介绍两种常见的复合类型:元组(Tuple)和数组(Array)。 Tuple(元组) 元组是Rust中的一种复合类型,…...
学习 JavaScript 闭包
1. 前言 闭包是 JavaScript 中一种非常重要的概念,它允许函数访问其外部作用域中的变量,即使在函数被返回或者在其原始定义的作用域之外执行时仍然可以访问这些变量。 在讲解闭包之前我们得弄清楚下面的概念: 作用域链: JavaSc…...
VScode中配置 C/C++ 环境 | IT拯救者
文章目录 0 引言1. 下载编辑器VScode2. 下载编译器MinGW并解压3. 将MinGW添加至环境变量4. 配置VScode插件5. 运行代码6. 调整和优化7. 提示8. 例行格式条款9. 例行格式条款 0 引言 由于VScode毛毛张使用不习惯,因此配置教程记不住,不过毛毛张看到一篇不…...
基于Python实现Midjourney集成到(个人/公司)平台中
目前Midjourney没有对外开放Api,想体验他们的服务只能在discord中进入他们的频道进行体验或者把他们的机器人拉入自己创建的服务器中;而且现在免费的也用不了了,想使用就得订阅。本教程使用midjourney-api这个开源项目,搭建Midjou…...
蓝桥杯刷题--python-6
0最大距离 - 蓝桥云课 (lanqiao.cn) n=int(input()) nums=list(map(int,input().split()))max_=float(-inf) for i in range (n):for j in range (i+1,n):tmp=abs(i-j)+abs(nums[i]-nums[j])max_=max(tmp,max_) print(max_) 0最长递增 - 蓝桥云课 (lanqiao.cn) import os im…...
node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
文章目录 ⭐前言⭐ 功能设计与实现💖 node后端操作数据库实现增删改查💖 vue3前端实现增删改查⭐ 效果⭐ 总结⭐ 结束⭐结束⭐前言 大家好,我是yma16,本文分享关于 node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查。 技术选型 前端:vite+vue3+antd 后端:…...
【Android】使用Apktool反编译Apk文件
文章目录 1. 下载Apktool1.1 Apktool官网下载1.2 百度网盘下载 2. 安装Apktool3. 使用Apktool3.1 配置Java环境3.2 准备Apk文件3.3 反编译Apk文件3.3.1 解包Apk文件3.3.2 修改Apk文件3.3.3 打包Apk文件3.3.4 签名Apk文件 1. 下载Apktool 要使用Apktool,需要准备好 …...
(04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
Hive中的排序通常涉及到order by 、sort by、distribute by 、cluster by 一、语法 selectcolumn1,column2, ... from table [where 条件] [group by column] [order by column] [cluster by column| [distribute by column] [sort by column] [limit [offset,] rows]; …...
Django模板(二)
标签if 标签在渲染过程中提供使用逻辑的方法,比如:if和for 标签被 {% 和 %} 包围,如下所示: 由于在模板中,没有办法通过代码缩进判断代码块,所以控制标签都需要有结束的标签 if判断标签{% if %} {% endif %} : # athlete_list 不为空 {% if athlete_list %}# 输出 ath…...
勒索病毒最新变种.faust勒索病毒来袭,如何恢复受感染的数据?
引言: 随着我们进入数字化时代,数据的重要性变得愈发显著,而网络安全威胁也日益增加。.faust勒索病毒是其中一种备受恶意分子钟爱的危险工具,它通过加密用户文件并勒索高额赎金来对个人和组织发起攻击。本文将深入探讨.faust勒索…...
python 人脸检测器
import cv2# 加载人脸检测器 关键文件 haarcascade_frontalface_default.xml face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# 读取图像 分析图片 ren4.png image cv2.imread(ren4.png) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 进行人脸…...
机器学习与深度学习
什么是机器学习 机器学习是一门跨学科的学科,它致力于研究和开发让计算机能够模拟人类学习行为的技术和方法。机器学习涉及多个学科的知识,如概率论、统计学、逼近论、凸分析、算法复杂度理论等,这些学科为机器学习提供了理论基础和数学工具…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
