LeetCode 2412.完成所有交易的初始最少钱数:【年度巨献】举例说明(讲明白),由难至简(手脚不乱),附Python一行版

ops/2025/2/2 22:32:28/

【LetMeFly】2412.完成所有交易的初始最少钱数:【年度巨献】举例说明(讲明白),由难至简(手脚不乱),附Python一行版

文章目录

  • 【LetMeFly】2412.完成所有交易的初始最少钱数:【年度巨献】举例说明(讲明白),由难至简(手脚不乱),附Python一行版
    • 问题描述
    • 解题方法:贪心
      • 那行,我们先选赔钱的
      • 到了能赚钱时候也不容易
      • 解法来了
    • 写法上的优化
    • 时空复杂度分析
    • AC代码(简化写法)
      • C++
      • Python
      • Java
      • Go

问题描述

力扣题目链接:https://leetcode.cn/problems/minimum-money-required-before-transactions/

给你一个下标从 0 开始的二维整数数组 transactions,其中transactions[i] = [costi, cashbacki] 。

数组描述了若干笔交易。其中每笔交易必须以 某种顺序 恰好完成一次。在任意一个时刻,你有一定数目的钱 money ,为了完成交易 i ,money >= costi 这个条件必须为真。执行交易后,你的钱数 money 变成 money - costi + cashbacki 

请你返回 任意一种 交易顺序下,你都能完成所有交易的最少钱数 money 是多少。

 

示例 1:

输入:transactions = [[2,1],[5,0],[4,2]]
输出:10
解释:
刚开始 money = 10 ,交易可以以任意顺序进行。
可以证明如果 money < 10 ,那么某些交易无法进行。

示例 2:

输入:transactions = [[3,0],[0,3]]
输出:3
解释:
- 如果交易执行的顺序是 [[3,0],[0,3]] ,完成所有交易需要的最少钱数是 3 。
- 如果交易执行的顺序是 [[0,3],[3,0]] ,完成所有交易需要的最少钱数是 0 。
所以,刚开始钱数为 3 ,任意顺序下交易都可以全部完成。

 

提示:

  • 1 <= transactions.length <= 105
  • transactions[i].length == 2
  • 0 <= costi, cashbacki <= 109

解题方法:贪心

“在运气和实力面前,我选择了实力”——小T如是说。

不论机遇多么坏,我都必将不负债。

那么,最“坏”的机遇是什么?当然是先亏钱( c o s t < c a s n b a c k cost \lt casnback cost<casnback)再赚钱,主打一个完事开头难。

那行,我们先选赔钱的

对于两笔赔钱投资[[6, 4], [3, 2]],有两种选择方案:

  1. 先选[6, 4],那么我们需要初始资金max(6, 6 + 3 - 4) = 5 = max(6, (6 - 4) + (3 - 2) + 2)
  2. 先选[3, 2],那么我们需要初始资金max(3, 3 + 6 - 2) = 7 = (3 - 2) + (6 - 4) + 4

有没有发现,不论选择方案如何,初始资金都为max(cost, 赔钱总额 + 最后一次投资的cashback)

想要初始资金最大,那么我们就最后选“cashback”最大的那次投资。

赔钱投资的最大初始资金要求为赔钱总额 + max(cashback_赔),最终剩余max(cashback_赔)开始进行赚钱投资。

到了能赚钱时候也不容易

好了,现在开始赚钱投资,钱开始越来越多了。但是,能赚钱不等于容易赚钱。想要赚钱,你得有那个资本。

既然接下来每一笔都会赚钱,也就是说每经过一笔交易所需的负担就会减小一些,所以我们不如上来就选最难的那个。

上来就选cost最大的那个,所需金额为max(cost_赚)

别忘了,赔钱阶段虽然赔钱,但是最终我们还剩下了max(cashback_赔)。因此还需要资金max(0, max(cashback_赔) - max(cost_赚))

解法来了

总结:两次遍历,第一次只遍历赔钱的交易,第二次遍历不赔钱的交易。

  • 对于赔钱的交易,所需初始资金ans = 赔钱总额 + max(cashback_赔)
  • 对于不赔钱交易,还需初始资金加上max(0, max(cashback_赔) - max(cost_赚))

下面先贴一个完整版代码,再放一个核心代码。

完整版代码:

/** @Author: LetMeFly* @Date: 2025-01-25 08:08:05* @LastEditors: LetMeFly.xyz* @LastEditTime: 2025-01-25 09:53:23*/
#ifdef _WIN32
#include "_[1,2]toVector.h"
#endif/*
先选赔的[[6, 4], [3, 2]]:先[6, 4]的话需要初始资金max(6, 6+3-4)=max(6, 5)=6=max(6, (6-4)+(3-2)+2)先[3, 2]的话需要初始资金max(3, 6+3-2)=max(3, 7)=7=(3-2)+(6-4)+4对于赔的:初始资金:赔钱总额+最大cashback结束资金:最大cashback
后选不赔的赚的一样:[[1, 2], [2, 3]],先[2, 3]再[1, 2]赚的不同:[[3, 5], [7, 8]]:先[3, 5]的话需要初始资金7-(5-3)=5先[7, 8]的话需要初始资金7反正就是只要钱到了最大的“cost”,钱就够了。如果先投资“cost”比较小的话,一定会赚,本金加上赚的钱达到最大cost就行,所以初始值所需较小。
*/
typedef long long ll;class Solution {
public:ll minimumMoney(vector<vector<int>>& transactions) {ll ans = 0;int M_pei = 0;// 先算赔的for (vector<int>& transaction : transactions) {if (transaction[1] < transaction[0]) {ans += transaction[0] - transaction[1];M_pei = max(M_pei, transaction[1]);}}ans += M_pei;// 再算赚的,初始资金M_peiint M_zhuan = 0;for (vector<int>& transaction : transactions) {if (transaction[1] >= transaction[0]) {M_zhuan = max(M_zhuan, transaction[0]);}}ans += max(M_zhuan - M_pei, 0);return ans;}
};#ifdef _WIN32
/*
[[2,1],[5,0],[4,2]]10
*/
/*
[[36,79],[94,94],[12,54],[71,25],[34,78],[89,66],[66,25],[7,29],[5,58],[2,25],[10,83],[62,62],[11,52],[40,5],[10,79],[74,53],[33,90],[91,81],[30,84]]270
*/// 暴力验证一个答案是否正确,复杂度O(len(t)!),对于10以内的数据还算可行
bool isok_baoliYanzheng(vector<vector<int>>& t, ll init) {sort(t.begin(), t.end());auto isok = [](vector<vector<int>>& t, ll init) {for (vector<int>& t0 : t) {if (init < t0[0]) {return false;}init += t0[1] - t0[0];}return true;};do {for (vector<int>& t0 : t) {cout << '(' << t0[0] << ", " << t0[1] << "), ";}cout << endl;bool isThisOk = isok(t, init);if (!isThisOk) {return false;}} while (next_permutation(t.begin(), t.end()));return true;
}int main() {string s;Solution sol;while (cin >> s) {vector<vector<int>> v = stringToVectorVector(s);debug(v);ll ans = sol.minimumMoney(v);dbg(ans);if (v.size() <= 10) {cout << "ifOk: ";fflush(stdout);cout << isok_baoliYanzheng(v, ans) << endl;}}return 0;
}
#endif

核心代码:

ll ans = 0;
int M_pei = 0;
// 先算赔的
for (vector<int>& transaction : transactions) {if (transaction[1] < transaction[0]) {ans += transaction[0] - transaction[1];M_pei = max(M_pei, transaction[1]);}
}
ans += M_pei;
// 再算赚的,初始资金M_pei
int M_zhuan = 0;
for (vector<int>& transaction : transactions) {if (transaction[1] >= transaction[0]) {M_zhuan = max(M_zhuan, transaction[0]);}
}
ans += max(M_zhuan - M_pei, 0);
return ans;

写法上的优化

上述代码的伪代码为:

ans = 0
M_pei = 0
for 赔钱交易 {ans += 赔钱总额M_pei = max(cashback)
}
ans += M_pei
M_zhuan = 0
for 不赔钱交易 {M_zhuan = max(cost)
}
if M_zhuan > M_pei {ans += M_zhuan - M_pei
}

我们不妨调换个顺序:

ans = 0
M_pei = 0
for 赔钱交易 {ans += 赔钱总额M_pei = max(cashback)
}
M_zhuan = 0
for 不赔钱交易 {M_zhuan = max(cost)
}
ans += M_pei  // 将这一行调换到这里
if M_zhuan > M_pei {ans += M_zhuan - M_pei
}

那么,最后4行的:

ans += M_pei
if M_zhuan > M_pei {ans += M_zhuan - M_pei
}

就可以简写为:

ans += max(M_zhuan, M_pei)

同时,前面10行的:

ans = 0
M_pei = 0
for 赔钱交易 {ans += 赔钱总额M_pei = max(cashback)
}
M_zhuan = 0
for 不赔钱交易 {M_zhuan = max(cost)
}

就可以缩减为一次遍历:

ans = 0
M_pei = 0
M_zhuan = 0
for 交易 {if cost > cashback {  // 赔钱ans += 赔钱总额M_pei = max(cashback)} else {M_zhuan = max(cost)}
}

不难发现,如果赔钱则cashback更小,如果不赔钱则cost更小。也就是说M_peiM_zhuan其实都是min(cost, cashback)的最大值。

因此上述代码还可以简写为:

ans = 0
M = 0
for 交易 {ans += max(0, cost - cashback)M = max(M, min(cost, cashback))
}

也就是说:

最终答案为:赔钱总额 + 最大的“min(cost, cashback)”

时空复杂度分析

  • 时间复杂度 O ( l e n ( t r a n s a c t i o n s ) ) O(len(transactions)) O(len(transactions))
  • 空间复杂度 O ( 1 ) O(1) O(1)

AC代码(简化写法)

C++

/** @Author: LetMeFly* @Date: 2025-01-25 09:58:52* @LastEditors: LetMeFly.xyz* @LastEditTime: 2025-01-25 09:59:57*/
typedef long long ll;class Solution {
public:ll minimumMoney(vector<vector<int>>& transactions) {ll ans = 0;int M = 0;for (vector<int>& t : transactions) {ans += max(0, t[0] - t[1]);M = max(M, min(t[0], t[1]));}return ans + M;}
};

Python

python">'''
Author: LetMeFly
Date: 2025-01-25 10:00:39
LastEditors: LetMeFly.xyz
LastEditTime: 2025-01-25 10:02:46
'''
from typing import Listclass Solution:def minimumMoney(self, transactions: List[List[int]]) -> int:return sum(max(0, c - e) for c, e in transactions) + max(min(t) for t in transactions)

Java

/** @Author: LetMeFly* @Date: 2025-01-25 10:05:00* @LastEditors: LetMeFly.xyz* @LastEditTime: 2025-01-25 10:06:21*/
class Solution {public long minimumMoney(int[][] transactions) {long ans = 0;int M = 0;for (int[] t : transactions) {ans += Math.max(0, t[0] - t[1]);M = Math.max(M, Math.min(t[0], t[1]));}return ans + M;}
}

Go

/** @Author: LetMeFly* @Date: 2025-01-25 10:07:19* @LastEditors: LetMeFly.xyz* @LastEditTime: 2025-01-25 10:12:39*/
package mainfunc max_MMRBT[T int](a T, b T) T {if a > b {return a}return b
}func min_MMRBT(a int, b int) int {if a < b {return a}return b
}func minimumMoney(transactions [][]int) (ans int64) {M := 0for _, t := range transactions {ans += int64(max_MMRBT(0, t[0] - t[1]))M = max_MMRBT(M, min_MMRBT(t[0], t[1]))}ans += int64(M)return
}

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

Tisfy:https://letmefly.blog.csdn.net/article/details/145352577


http://www.ppmy.cn/ops/155139.html

相关文章

MySQL(导入sql文件)

传文件省略…(从windows传到linux) 改编码格式 为什么不在windows里面修改呢&#xff1f;因为windows打开发现根本打不开直接就卡住了数据过多了&#xff08;4百万数据(不信可以自己试一下)&#xff09; [rootCentOS8 ~]# file order_info.sql order_info.sql: UTF-8 Unicode…

爬取鲜花网站数据

待爬取网页&#xff1a; 代码&#xff1a; import requestsfrom lxml import etree import pandas as pdfrom lxml import html import xlwturl "https://www.haohua.com/xianhua/"header {"accept":"image/avif,image/webp,image/apng,image/sv…

智能汽车网络安全威胁报告

近年来随着智能汽车技术的快速发展&#xff0c;针对智能汽车的攻击也逐渐从传统的针对单一车辆控制器的攻击转变为针对整车智能化服务的攻击&#xff0c;包括但不限于对远程控制应用程序的操控、云服务的渗透、智能座舱系统的破解以及对第三方应用和智能服务的攻击。随着WP.29 …

Python 梯度下降法(七):Summary

文章目录 Python 梯度下降法&#xff08;七&#xff09;&#xff1a;Summary一、核心思想1.1 核心思想1.2 优化方法概述1.3 第三方库的使用 二、 BGD2.1 介绍2.2 torch 库算法2.2 代码示例2.3 SGD2.4 SGD代码示例2.5 MBGD2.6 MBGD 代码示例 三、 Adagrad3.1 介绍3.2 torch 库算…

如何使用 DeepSeek API 结合 VSCode 提升开发效率

引言 在当今的软件开发领域&#xff0c;API 的使用已经成为不可或缺的一部分。DeepSeek 是一个强大的 API 平台&#xff0c;提供了丰富的功能和数据&#xff0c;可以帮助开发者快速构建和优化应用程序。而 Visual Studio Code&#xff08;VSCode&#xff09;作为一款轻量级但功…

《苍穹外卖》项目学习记录-Day10订单状态定时处理

利用Cron表达式生成器生成Cron表达式 1.处理超时订单 查询订单表把超时的订单查询出来&#xff0c;也就是订单的状态为待付款&#xff0c;下单的时间已经超过了15分钟。 //select * from orders where status ? and order_time < (当前时间 - 15分钟) 遍历集合把数据库…

人工智能入门课【手写自注意力机制】

原理 自注意力&#xff08;Self-Attention&#xff09;是一种强大的机制&#xff0c;广泛应用于自然语言处理、计算机视觉等领域&#xff0c;尤其是在Transformer架构中发挥了关键作用。它的核心思想是让模型能够动态地关注输入序列中不同位置之间的关系&#xff0c;从而更好地…

RDMA 工作原理 | 支持 RDMA 的网络协议

注&#xff1a;本文为 “RDMA” 相关文章合辑。 英文引文机翻未校。 图片清晰度受引文所限。 Introduction to Remote Direct Memory Access (RDMA) Written by: Dotan Barak on March 31, 2014.on February 13, 2015. What is RDMA? 什么是 RDMA&#xff1f; Direct me…