C++算法:2499让数组不相等的最小总代价

题目

给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,两者长度都为 n 。每次操作中,你可以选择交换 nums1 中任意两个下标处的值。操作的 开销 为两个下标的和。你的目标是对于所有的 0 <= i <= n - 1 ,都满足 nums1[i] != nums2[i] ,你可以进行 任意次 操作,请你返回达到这个目标的 最小 总代价。请你返回让 nums1 和 nums2 满足上述条件的 最小总代价 ,如果无法达成目标,返回 -1 。

术语

如果nums1[i] 和nums2[i]相等,是内部;否则是外部。显然,内部的数需要全部交换。

假定不同的数的数量是偶数

分析那些情况可以只内部交换,不和外包交换。下面只列出nums1和nums2中不同的元素。

nums1

nums2

交换过程

能否能内部交换

[1,2]

[1,2]

[2,1]

[1,2,2,2]

[1,2,2,2]

[2,1,2,2]

不能

[1,2,3,4]

[1,2,3,4]

[2,1,3,4][2,1,4,3]

[1,1,2,3]

[1,1,2,3]

[2,1,1,3][2,3,1,1]

[1,1,2,2]

[1,1,2,2]

[2,1,1,2][2,2,1,1]

猜想:除众数外,其它数都可以匹配上

如果众数大于总数的一半,则非众数全部和众数匹配,非众数全部匹配,部分众数无法匹配,假定其为iNeedSwapOut则其值为:众数-非众数。

如果众数小于等于总数的一半(意味着至少2种数)。假定最多的数数量为n1,次多的为n2,如果有三种数,第三多的为n3。

最多的数和次多的数匹配,也就是n1和n2的各减少1。用N1代表新的n1,N2代码新的n2.结果有三种:

转换前

转换后

情况一

[1,2]

[]

情况二

[1,2,3,4]

[3,4]

[1,1,2,2,3,3]

[3,3,1,2]

[1,1,1,2,2,2,3,3,3,4]

[3,3,3,1,1,2,2,4]

情况三

[1,1,2,3]

[1,3]

  • N1和N2都为0,匹配结束。由于和是偶数,每次匹配和减少2,最终一定为成为0。
  • N1为n1-1,由于2*n1 <= sum ,所以 2*(n1-1) <= sum-2。 匹配后,仍然符合:众数小于等于总数的一半。
  • N1为n3,前提条件 n1和n2和n3相等,且都大于0。其和至少为n3*3-2。假的2*n3 > n3*3-2,则0 >n3-2,即n < 2。

n1为1时,此时有偶数个数量为1的种类,任意匹配都符合题意。

结论

如果众数小于等于总数的一半,则所有的数都可以内部匹配。如果众数大于总数的一半,则部分众数需要在外部匹配。

如果不同数的数量是奇数

由于是奇数,众数的数量不会等于总数的一半。

如果众数小于总数的一半

用C++的整数表示就是:modeCount <= iNotSameCount/2 ,推论:至少有3个数。n1,n2,n3各取一个数,余下的数,符合偶数且众数小于等于总数的一半。这三个数,至少有一个数,和当前的nums1[0]、nums2[i]不同,假定为x,另外两个数分别为y1,y2。分三步:一,先交换除x,y1,y2外的数。二,再交换y1,y2。三,交换nums1[i]和x。由于x和y1 y2 原始nums1[i]和nums2[i]都不同,所以完成第二步后nums1[0]和nums2[0]一定和x不同。

如果众数大于总数的一半

多出的部分外部匹配。其它内部匹配。

结论

如果众数小于等于总数的一半(整除2),则全部内交换。否则需要外部交换,需要的交换次数可以用统一的公式。

int iNeedSwapOut = max(0,modeCount - (iNotSameCount - modeCount));

代码结构

代码分三部分。

  • 计算内部的数量,需要交换的数的数量。内部交换的总代价。
  • 计算需要交换的数的众数(数量最多的数)。
  • 计算外部交换的总代价。
  • 代码

class Solution {

public:

long long minimumTotalCost(vector<int>& nums1, vector<int>& nums2) {

m_c = nums1.size();

int iNotSameCount = 0;

long long llRet = 0;

std::unordered_map<int, int> mNotSameNum;

for (int i = 0; i < m_c; i++)

{

if (nums1[i] != nums2[i])

{

continue;

}

llRet += i;

mNotSameNum[nums1[i]]++;

iNotSameCount++;

}

if (mNotSameNum.empty())

{

return 0;

}

//如果众数不超过一半(意味着至少2个数),如果是偶数,内部就可以交换。

//如果是奇数,至少有一个和nums2[0]不同。

//求众数和众数出现的数量

int mode = -1;

int modeCount = 0;

for (const auto& [v, n] : mNotSameNum)

{

if (n > modeCount)

{

mode = v;

modeCount = n;

}

}



//除众数外,其它数都可以内部交换,非众数都可以和众数交换

int iNeedSwapOut = max(0,modeCount - (iNotSameCount - modeCount));

for (int i = 0; (i < m_c)&& iNeedSwapOut; i++)

{//必须从小到循环,这样才能代价最小

if (nums1[i] == nums2[i])

{//已经是内部

continue;

}

if ((mode == nums1[i]) || (mode == nums2[i]))

{//不能和众数交换,或者i或交换的元素相等

continue;

}

iNeedSwapOut--;

llRet += i;

}

return iNeedSwapOut ? -1 : llRet ;

}

int m_c;

};

说明

源码和少量测试用例下载:
https://download.csdn.net/download/he_zhidan/88368465

https://img-blog.csdnimg.cn/ea2601b3918f4aef836b5fe30da2ebf7.gif#pic_center#pic_center

其它

视频课程

如果你觉得复杂,想从简单的算法开始,可以学习我的视频课程。

https://edu.csdn.net/course/detail/38771

我的其它课程

https://edu.csdn.net/lecturer/6176

测试环境

win7 VS2019 C++17 或Win10 VS2022 Ck++17

相关下载

算法精讲《闻缺陷则喜算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

作者人生格言

有所得,以墨记之,故曰墨家

闻缺陷则喜。问题发现得越早,越给老板省钱。

算法是程序的灵魂

https://img-blog.csdnimg.cn/f95ddae62a4e43a68295601c723f92fb.gif#pic_center

有所得,以墨记之,故曰墨家

闻缺陷则喜。问题发现得越早,越给老板省钱。

算法是程序的灵魂

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值