Minimum Money Required Before Transactions
Minimum Money Required Before Transactions
You are given a 0-indexed 2D integer array transactions , where transactions[i] = [costi, cashbacki] .
The array describes transactions, where each transaction must be completed exactly once in some order. At any given moment, you have a certain amount of money . In order to complete transaction , must hold true. After performing a transaction, money becomes .
Return the minimum amount of money required before any transaction so that all of the transactions can be completed regardless of the order of the transactions.
Example 1:
Input: transactions = [[2,1],[5,0],[4,2]] Output: 10 Explanation: Starting with money = 10, the transactions can be performed in any order. It can be shown that starting with money < 10 will fail to complete all transactions in some order.
Example 2:
Input: transactions = [[3,0],[0,3]] Output: 3 Explanation: - If transactions are in the order [[3,0],[0,3]], the minimum money required to complete the transactions is 3. - If transactions are in the order [[0,3],[3,0]], the minimum money required to complete the transactions is 0. Thus, starting with money = 3, the transactions can be performed in any order.
Constraints:
解题思路
对于某种顺序确定后如何求最大值呢,假设已经完成了前笔交易,那么第笔的交易应该要满足即
要取最小值,那么就要遍历所有情况,取不等式右边的最大值。那么最大值会出现在哪一种情况呢,从集合的角度来看,根据的不同把方案分成若干个集合,不等式右边的最大值一定会在其中一个集合出现。接着枚举,当固定后,要使得右式取最大值等价于求的最大值,当每一项都是正数时,即,就可以取到最大值。因此一开始可以枚举 transactions 数组,把的交易累加,就可以取到最大值,然后枚举,如果有那么总和减去再加上就可以快速算出不等式右边的值。
AC代码如下:
1 class Solution { 2 public: 3 long long minimumMoney(vector<vector<int>>& transactions) { 4 long long sum = 0; 5 for (auto &p : transactions) { 6 if (p[0] - p[1] > 0) sum += p[0] - p[1]; 7 } 8 long long ret = 0; 9 for (auto &p : transactions) { 10 long long t = sum; 11 if (p[0] - p[1] > 0) t -= p[0] - p[1]; 12 ret = max(ret, t + p[0]); 13 } 14 return ret; 15 } 16 };
还有一种做法,非常感谢laj大佬提供的思路。
先说一下算法的执行过程,首先根据的正负性分成两组,其中对于这组的元素按照进行升序排序,对于这组的元素按照进行降序排序。然后对这两组进行合并,其中的在前部分,的在后部分。接下来枚举每一个元素,用来表示上一次交易结束后剩余的金额,对于某个交易,如果有,意味着初始时至少要再补这么多的金额,同时需要把更新成,然后更新。最后返回答案(也就是每次交易前要补的金额的总和)。
下面来证明这种做法是对的。
要使得能完成所有交易的最少钱数最大,意味着每次交易前所需的金额应尽可能的大,即在第笔交易前所需的金额应尽可能的大。那么是否存在一种交易顺序,使得在某笔交易前的能取到最大值呢?
在所有交易顺序构成的集合中,我们先考虑其中一个子集,这个子集内的交易顺序都满足在前部分,的在后部分,现在要证明能完成所有交易的最少钱数最大值必定在这个子集中。假设在某个交易顺序中,第笔交易满足,第笔交易满足。现在来看看交换第笔和第笔交易前后所需的金额大小,需要注意的是,交换后只会对进行第笔和第笔交易前所需的金额数产生影响,其他笔交易的所需金额不会发生改变。
进行第笔交易前需要的金额 | 进行第笔交易前需要的金额 | |
交换前 | ||
交换后 |
化简,对每个式子减去,得到:
进行第笔交易前需要的金额 | 进行第笔交易前需要的金额 | |
交换前 | ||
交换后 |
可以发现,由于,因此有;由于,因此有,即交换后的两笔交易所需的金额比交换前的要大,因此更大的金额会出现在交换后的交易顺序中。因此对于某个交易顺序,只要发现前一笔交易的,而后一笔交易,那么我们就交换这两笔交易的顺序,这样总是可以把交易的顺序变成在前部分,的在后部分,且所需的交易金额会变大。
现在我们把答案的范围缩小到这个子集中,再去看看在这个子集中,答案是否存在于某个更小的子集中。
先考虑交易顺序的前部分,即满足的部分。假设某个交易顺序中第笔交易和第笔交易满足,现在来看看交换第笔和第笔交易前后所需的金额大小(与上面得到的结果一样):
进行第笔交易前需要的金额 | 进行第笔交易前需要的金额 | |
交换前 | ||
交换后 |
由于此时,因此,同时又因为,因此,即交换后的第笔交易前需要的金额都比交换前的两笔交易所需的金额要大,因此更大的金额会出现在交换后的交易顺序中,即出现在满足的交易顺序中。因此在交易顺序的前部分,如果发现前一笔交易的比后一笔交易的大,那么就交换这两笔交易。
同理可证对于交易的后部分,即满足的部分,更大的交易金额会出现在满足的交易顺序中。
因此最大的交易金额必定会出现在这样的交易顺序中:交易顺序满足在前部分,的在后部分,在前部分中满足,在后部分中满足。
AC代码如下:
1 class Solution { 2 public: 3 long long minimumMoney(vector<vector<int>>& transactions) { 4 vector<vector<int>> v1, v2; 5 for (auto &p : transactions) { 6 if (p[0] - p[1] >= 0) v1.push_back(p); 7 else v2.push_back(p); 8 } 9 10 sort(v1.begin(), v1.end(), [&](vector<int> &a, vector<int> &b) { 11 return a[1] < b[1]; 12 }); 13 sort(v2.begin(), v2.end(), [&](vector<int> &a, vector<int> &b) { 14 return a[0] > b[0]; 15 }); 16 v1.insert(v1.end(), v2.begin(), v2.end()); 17 18 long long ret = 0, r = 0; 19 for (auto &p : v1) { 20 if (r < p[0]) { 21 ret += p[0] - r; 22 r = p[0]; 23 } 24 r -= p[0] - p[1]; 25 } 26 27 return ret; 28 } 29 };
当然也可以按照上面证明的过程那样去求最大值,AC代码如下:
1 class Solution { 2 public: 3 long long minimumMoney(vector<vector<int>>& transactions) { 4 vector<vector<int>> v1, v2; 5 for (auto &p : transactions) { 6 if (p[0] - p[1] >= 0) v1.push_back(p); 7 else v2.push_back(p); 8 } 9 10 sort(v1.begin(), v1.end(), [&](vector<int> &a, vector<int> &b) { 11 return a[1] < b[1]; 12 }); 13 sort(v2.begin(), v2.end(), [&](vector<int> &a, vector<int> &b) { 14 return a[0] > b[0]; 15 }); 16 v1.insert(v1.end(), v2.begin(), v2.end()); 17 18 long long ret = 0, r = 0; 19 for (auto &p : v1) { 20 ret = max(ret, r + p[0]); 21 r += p[0] - p[1]; 22 } 23 24 return ret; 25 } 26 };
参考资料
y总,第一名是谁,可以讲一下第一名的代码吗?力扣第87场双周赛:https://www.bilibili.com/video/BV1FV4y1u79H
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16708819.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效