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

【Leetcode】2561. 重排水果

文章目录

  • 题目
  • 思路
      • 步骤1: 差异计算与可行性检查
      • 步骤2: 收集需要移动的水果
      • 步骤3: 贪心配对与成本计算(核心优化)
      • 整体优势
  • 代码
    • C++
    • Java
    • Python
  • 复杂度分析
    • 时间复杂度
    • 空间复杂度
  • 结果
  • 总结

题目

题目链接🔗

你有两个果篮,每个果篮中有 n 个水果。给你两个下标从 0 开始的整数数组 basket1basket2,用以表示两个果篮中每个水果的交换成本。你想要让两个果篮相等。为此,可以根据需要多次执行下述操作:

  • 选中两个下标 i 和 j,并交换 basket1 中的第 i 个水果和 basket2 中的第 j 个水果。
  • 交换的成本是 min(basket1[i], basket2[j])
  • 根据果篮中水果的成本进行排序,如果排序后结果完全相同,则认为两个果篮相等。

返回使两个果篮相等的最小交换成本,如果无法使两个果篮相等,则返回 -1。

示例 1:

输入:basket1 = [4,2,2,2], basket2 = [1,4,1,2]
输出:1
解释:交换 basket1 中下标为 1 的水果和 basket2 中下标为 0 的水果,交换的成本为 1。此时,basket1 = [4,1,2,2]basket2 = [2,4,1,2]。重排两个数组,发现二者相等。

示例 2:

输入:basket1 = [2,3,4,1], basket2 = [3,2,5,1]
输出:-1
解释:可以证明无法使两个果篮相等。

提示:

  • basket1.length == basket2.length
  • 1 <= basket1.length <= 10^5
  • 1 <= basket1[i], basket2[i] <= 10^9

思路

这个问题本质上是平衡两个果篮中水果分布的最小成本问题。我们可以通过交换操作“移动”水果,使两个篮子的排序后内容相同。优化思路基于贪心算法,重点在差异计算和配对策略上。

步骤1: 差异计算与可行性检查

  • 使用一个哈希表直接计算每个水果在basket1和basket2的数量差值(diff = count1 - count2)。
  • 检查可行性:对于每种水果,diff必须是偶数(因为需要平衡diff/2个实例)。如果任何diff不是偶数,返回-1。这确保总数量为偶数,且可平衡。
  • 优势:简化了原先的合并总数量步骤,直接操作差值,减少计算开销。

步骤2: 收集需要移动的水果

  • 对于diff > 0的水果,从basket1“移出”diff/2个实例(放入swap1列表,表示basket1的多余供给)。
  • 对于diff < 0的水果,从basket2“移出”-diff/2个实例(放入swap2列表,表示basket2的多余供给)。
  • 同时,找到全局最小成本水果globalMin,用于潜在的中转交换。

步骤3: 贪心配对与成本计算(核心优化)

  • 将swap1升序排序(从小到大),swap2降序排序(从大到小)。
  • 逐一配对:对于每个pair (a = swap1[i], b = swap2[i]),成本 = min(min(a, b), 2 * globalMin)。
    • 直接交换成本:min(a, b)。
    • 中转交换成本:2 * globalMin(使用最小水果作为媒介,进行两次交换)。
  • 为什么这种配对是最优的?
    • 这是一种贪心策略:将最小供给 (小a) 与最大需求 (大b) 配对,确保min(a, b) 倾向于小值,同时中转选项覆盖高成本情况。
    • 证明:假设我们有供给序列S和需求序列D,成本函数f(x, y) = min(min(x,y), 2*min)。反向排序配对最小化总成本,因为它优先让小值决定直接成本,或用中转“封顶”高成本(类似于分配问题中的贪心证明:排序后配对能最小化sum of min)。
    • 如果随机配对,可能导致小值与小值配对(浪费低成本机会)或大值与大值配对(被迫支付高直接成本)。此策略确保全局最小。
  • 边缘情况:如果swap1和swap2长度不相等(理论上不会,因为总diff平衡),或所有diff=0(成本0)。

整体优势

  • 时间复杂度O(n + m log m),m为移动数量(<= n/2),适合n=1e5。
  • 思路简洁,具有扩展性(如多中转选项可升级为优先队列)。

代码

C++

#include <bits/stdc++.h>
using namespace std;class Solution {
public:long long minCost(vector<int>& basket1, vector<int>& basket2) {unordered_map<int, int> cnt;int n = basket1.size();// 步骤1: 统计差值for (int i = 0; i < n; ++i) {cnt[basket1[i]]++;cnt[basket2[i]]--;}// 检查可行性for (auto& p : cnt) {if (p.second % 2 != 0) return -1;}vector<int> swap1, swap2;int globalMin = INT_MAX;for (int fruit : basket1) globalMin = min(globalMin, fruit);for (int fruit : basket2) globalMin = min(globalMin, fruit);// 步骤2: 收集需要移动的水果for (auto& p : cnt) {int fruit = p.first;int diff = p.second;if (diff > 0) {swap1.insert(swap1.end(), diff / 2, fruit);} else if (diff < 0) {swap2.insert(swap2.end(), -diff / 2, fruit);}}// 步骤3: 贪心配对sort(swap1.begin(), swap1.end());sort(swap2.rbegin(), swap2.rend());long long cost = 0;for (size_t i = 0; i < swap1.size(); ++i) {cost += min(2LL * globalMin, min((long long)swap1[i], (long long)swap2[i]));}return cost;}
};

Java

import java.util.*;class Solution {public long minCost(int[] basket1, int[] basket2) {Map<Integer, Integer> cnt = new HashMap<>();int n = basket1.length;// 步骤1: 统计差值for (int i = 0; i < n; i++) {cnt.merge(basket1[i], 1, Integer::sum);cnt.merge(basket2[i], -1, Integer::sum);}// 检查可行性for (int diff : cnt.values()) {if (diff % 2 != 0) return -1;}List<Integer> swap1 = new ArrayList<>();List<Integer> swap2 = new ArrayList<>();int globalMin = Integer.MAX_VALUE;for (int fruit : basket1) globalMin = Math.min(globalMin, fruit);for (int fruit : basket2) globalMin = Math.min(globalMin, fruit);// 步骤2: 收集需要移动的水果for (Map.Entry<Integer, Integer> entry : cnt.entrySet()) {int fruit = entry.getKey();int diff = entry.getValue();if (diff > 0) {swap1.addAll(Collections.nCopies(diff / 2, fruit));} else if (diff < 0) {swap2.addAll(Collections.nCopies(-diff / 2, fruit));}}// 步骤3: 贪心配对Collections.sort(swap1);swap2.sort(Collections.reverseOrder());long cost = 0;for (int i = 0; i < swap1.size(); i++) {cost += Math.min(2L * globalMin, Math.min(swap1.get(i), swap2.get(i)));}return cost;}
}

Python

from typing import List
from collections import Counterclass Solution:def minCost(self, basket1: List[int], basket2: List[int]) -> int:# 步骤1: 统计差值cnt = Counter(basket1)cnt.subtract(Counter(basket2))# 检查可行性for diff in cnt.values():if diff % 2 != 0:return -1swap1 = []swap2 = []global_min = min(min(basket1), min(basket2))# 步骤2: 收集需要移动的水果for fruit, diff in cnt.items():if diff > 0:swap1.extend([fruit] * (diff // 2))elif diff < 0:swap2.extend([fruit] * (-diff // 2))# 步骤3: 贪心配对swap1.sort()swap2.sort(reverse=True)cost = 0for a, b in zip(swap1, swap2):cost += min(2 * global_min, min(a, b))return cost

复杂度分析

时间复杂度

  • 统计差值:O(n),n为果篮长度。
  • 检查和收集:O(k),k为不同水果种类。
  • 排序和配对:O(m log m),m为移动水果数量(<= n/2)。
  • 总时间:O(n + m log m),高效处理n=1e5。

空间复杂度

  • 哈希表和列表:O(k + m),k <= n,m <= n/2,空间友好。

结果

该算法正确计算最小交换成本,或在不可行时返回-1。通过示例1:成本1;示例2:-1。

总结

这个题解通过优化后的贪心思路(差异计算 + 反向排序配对 + 中转机制)解决了问题,强调了算法的最优性证明。

http://www.xdnf.cn/news/16967.html

相关文章:

  • Paper Reading《TrafficFormer: An Efficient Pre-trained Model for Traffic Data》
  • 【Leetcode hot 100】49.字母异位词分组
  • Windows中使用Qwen模型:VSCode+Cline
  • ABP VNext + NATS JetStream:高性能事件流处理
  • 【智能体cooragent】不同的单智能体调用的大模型的推理的输入与输出
  • flutter分享到支付宝
  • 模拟激光相机工作站版本6.0 5.2.32 6.0.44 6.031 5.2.20
  • LeetCode 每日一题 2025/7/28-2025/8/3
  • gcc-arm-none-eabi安装后,找不到libgcc.a的拉置
  • Java基础暑假每日一练
  • 集成电路学习:什么是CMSIS微控制器软件接口标准
  • Json Jsoncpp
  • sqli-labs:Less-20关卡详细解析
  • Gossip 协议
  • 用 Qt 打造优雅的密码输入框:添加右侧眼睛图标切换显示
  • 关于Web前端安全防御之点击劫持的原理及防御措施
  • OpenCV HSV与RGB颜色模型的区别
  • Elasticsearch+Logstash+Filebeat+Kibana单机部署
  • 论文笔记:Bundle Recommendation and Generation with Graph Neural Networks
  • OpenCV 全解读:核心、源码结构与图像/视频渲染能力深度对比
  • 电力系统分析笔记:发电机与变压器的数学建模与运行状态详解
  • 图漾AGV行业常用相机使用文档
  • Unity —— Android 应用构建与发布​
  • 边缘计算优化!陌讯轻量化模型实现路面裂缝误检率↓78%
  • Java函数式编程之【Stream终止操作】【中】【通用约简reduce】
  • 机器学习sklearn:聚类
  • Python编程基础与实践:Python函数编程入门
  • 通过解决docker network connect实现同一个宿主机不同网络的容器间通信
  • Flutter dart运算符
  • synchronized 深度剖析:从语法到锁升级的完整演进