【动态规划】【记忆化搜索】【状态压缩】1681. 最小不兼容性

这篇具有很好参考价值的文章主要介绍了【动态规划】【记忆化搜索】【状态压缩】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 = iIndex
m_c + iPre;
setRet.insert(m_vMaskGroupFirstToMin[iIndex]);
}
int iMin = setRet.begin();
return (m_iNotMay == iMin) ? -1 : iMin;
}
int JoinMask(int iSelMask, int iNewSelIndex)
{
return iSelMask
m_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;
};
【动态规划】【记忆化搜索】【状态压缩】1681. 最小不兼容性,# 算法题,动态规划,算法,c++,力扣,记忆化搜索,状态压缩,最小不兼容性、

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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. 最小不兼容性,# 算法题,动态规划,算法,c++,力扣,记忆化搜索,状态压缩,最小不兼容性、文章来源地址https://www.toymoban.com/news/detail-827370.html

到了这里,关于【动态规划】【记忆化搜索】【状态压缩】1681. 最小不兼容性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 图论10-哈密尔顿回路和哈密尔顿路径+状态压缩+记忆化搜索

    求解哈密尔顿回路 如何求解一个图是否存在哈密尔顿回路呢? 一个最直观的想法就是暴力求解。暴力求解的思路也很简单:我们遍历图的每一个顶点 v,然后从顶点 v 出发,看是否能够找到一条哈密尔顿回路。 暴力求解的代价同求解全排列问题是等价的,其时间复杂度为

    2024年02月04日
    浏览(36)
  • 掷骰子(从暴力搜索 到 记忆化搜索 到 动态规划)

    LeetCode上的 掷骰子模拟 题目描述通常如下: 题目:掷骰子模拟(Simulation of Dice Rolling) 原题链接 描述: 有一个骰子模拟器,每次投掷时都会生成一个1到6的随机数。不过,在使用这个模拟器时有一个约束条件:连续掷出数字i的次数不能超过rollMax[i](其中i从1开始编号)。

    2024年03月19日
    浏览(60)
  • 周赛379(排序、分类讨论、记忆化搜索(动态规划))

    简单 给你一个下标从 0 开始的二维整数数组 dimensions 。 对于所有下标 i ( 0 = i dimensions.length ), dimensions[i][0] 表示矩形 i 的长度,而 dimensions[i][1] 表示矩形 i 的宽度。 返回对角线最 长 的矩形的 面积 。如果存在多个对角线长度相同的矩形,返回面积最 大 的矩形的面积。

    2024年01月19日
    浏览(41)
  • [动态规划及递归记忆搜索法]1.钢条切割问题

    摘要 本系列从6道经典的动态规划题入手,去理解动态规划的基本思路和想法,以及动态规划和递归记忆搜索法存在的某些联系,对于每道题目,我们将用两种方法去实现,这里讲解第一道题目,作个开头。 前言 我们知道,大部分的动态规划题目可以利用递归加记忆搜索法去

    2024年02月03日
    浏览(46)
  • 跳跃游戏 (DFS->记忆化搜索->动态规划/贪心证明)

            跳跃游戏是一种典型的算法题目,经常是给定一数组arr,从数组的某一位置i出发,根据一定的跳跃规则,比如从i位置能跳arr[i]步,或者小于arr[i]步,或者固定步数,直到到达某一位置,可能是数组的最后一个位置,也有可能是某一特别的数值处,也有可能在这个

    2024年02月03日
    浏览(36)
  • 【每日一题Day95】LC1815得到新鲜甜甜圈的最多组数 | 状态压缩dp 记忆化搜索

    有一个甜甜圈商店,每批次都烤 batchSize 个甜甜圈。这个店铺有个规则,就是在烤一批新的甜甜圈时,之前 所有 甜甜圈都必须已经全部销售完毕。给你一个整数 batchSize 和一个整数数组 groups ,数组中的每个整数都代表一批前来购买甜甜圈的顾客,其中 groups[i] 表示这一批顾客

    2024年02月03日
    浏览(36)
  • 【LeetCode:72. 编辑距离 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月06日
    浏览(51)
  • 【动态规划】【记忆化搜索】C++算法:546移除盒子

    视频算法专题 动态规划汇总 记忆化搜索 给出一些不同颜色的盒子 boxes ,盒子的颜色由不同的正数表示。 你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k = 1),这样一轮之后你将得到 k * k 个积分。 返回 你能

    2024年01月16日
    浏览(35)
  • 蓝桥杯-回路计数(状态压缩、动态规划)

    蓝桥学院由 21 21 21 栋教学楼组成,教学楼编号 11 11 11 ​​ 到 21 21 21 ​​。对于两栋教学楼 a a a 和 b b b ​,当 a a a 和 b b b 互质时, a a a 和 b b b 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。 小蓝现在在第一栋教学楼,他想要访问每栋教学楼正

    2024年02月08日
    浏览(58)
  • 【动态规划】【记忆化搜索】【C++算法】664. 奇怪的打印机

    视频算法专题 动态规划汇总 记忆化搜索 字符串 有台奇怪的打印机有以下两个特殊要求: 打印机每次只能打印由 同一个字符 组成的序列。 每次可以在从起始到结束的任意位置打印新字符,并且会覆盖掉原来已有的字符。 给你一个字符串 s ,你的任务是计算这个打印机打印

    2024年01月21日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包