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 i, moneycosti must hold true. After performing a transaction, money becomes moneycosti+cashbacki.

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:

1transactions.length105
transactions[i].length==2
0costi,cashbacki109

 

解题思路

  对于某种顺序确定后如何求最大值呢,假设已经完成了前i1笔交易,那么第i笔的交易应该要满足money(a0b0)(a1b1)(ai1bi1)aimoney(a0b0)+(a1b1)++(ai1+bi1)+ai

  money要取最小值,那么就要遍历所有情况,取不等式右边的最大值。那么最大值会出现在哪一种情况呢,从集合的角度来看,根据ai的不同把方案分成若干个集合,不等式右边的最大值一定会在其中一个集合出现。接着枚举ai,当ai固定后,要使得右式取最大值等价于求(a0b0)+(a1b1)++(ai1+bi1)的最大值,当每一项都是正数时,即akbk>0,就可以取到最大值。因此一开始可以枚举 transactions 数组,把akbk>0的交易累加,就可以取到最大值,然后枚举ai,如果有aibi>0那么总和减去aibi再加上ai就可以快速算出不等式右边的值。

  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大佬提供的思路。

  先说一下算法的执行过程,首先根据aibi的正负性分成两组,其中对于aibi0这组的元素按照bi进行升序排序,对于aibi<0这组的元素按照ai进行降序排序。然后对这两组进行合并,其中aibi0的在前部分,aibi<0的在后部分。接下来枚举每一个元素,用r来表示上一次交易结束后剩余的金额,对于某个交易i,如果有r<ai,意味着初始时至少要再补air这么多的金额,同时需要把r更新成ai,然后更新r=rai+bi。最后返回答案(也就是每次交易前要补的金额的总和)。

  下面来证明这种做法是对的。

  要使得能完成所有交易的最少钱数最大,意味着每次交易前所需的金额应尽可能的大,即在第i笔交易前所需的金额k=1i1(akbk)+ai应尽可能的大。那么是否存在一种交易顺序,使得在某笔交易前的k=1i1(akbk)+ai能取到最大值呢?

  在所有交易顺序构成的集合中,我们先考虑其中一个子集,这个子集内的交易顺序都满足aibi0在前部分,aibi<0的在后部分,现在要证明能完成所有交易的最少钱数最大值必定在这个子集中。假设在某个交易顺序中,第i笔交易满足aibi<0,第i+1笔交易满足ai+1bi+10。现在来看看交换第i笔和第i+1笔交易前后所需的金额大小,需要注意的是,交换后只会对进行第i笔和第i+1笔交易前所需的金额数产生影响,其他笔交易的所需金额不会发生改变。

  进行第i笔交易前需要的金额 进行第i+1笔交易前需要的金额
交换前 k=1i1(akbk)+ai k=1i(akbk)+ai+1
交换后 k=1i1(akbk)+ai+1 k=1i1(akbk)+ai+1bi+1+ai

化简,对每个式子减去k=1i1(akbk),得到:

  进行第i笔交易前需要的金额 进行第i+1笔交易前需要的金额
交换前 ai aibi+ai+1
交换后 ai+1 ai+1bi+1+ai

  可以发现,由于aibi<0,因此有ai+1>aibi+ai+1;由于aibi0,因此有ai+1bi+1+aiai,即交换后的两笔交易所需的金额比交换前的要大,因此更大的金额会出现在交换后的交易顺序中。因此对于某个交易顺序,只要发现前一笔交易的aibi<0,而后一笔交易ai+1bi+10,那么我们就交换这两笔交易的顺序,这样总是可以把交易的顺序变成aibi0在前部分,aibi<0的在后部分,且所需的交易金额会变大。

  现在我们把答案的范围缩小到这个子集中,再去看看在这个子集中,答案是否存在于某个更小的子集中。

  先考虑交易顺序的前部分,即满足aibi0的部分。假设某个交易顺序中第i笔交易和第i+1笔交易满足bi>bi+1,现在来看看交换第i笔和第i+1笔交易前后所需的金额大小(与上面得到的结果一样):

  进行第i笔交易前需要的金额 进行第i+1笔交易前需要的金额
交换前 ai aibi+ai+1
交换后 ai+1 ai+1bi+1+ai

  由于此时ai+1bi+10,因此ai+1bi+1+aiai,同时又因为bi>bi+1,因此ai+1bi+1+ai>aibi+ai+1,即交换后的第i+1笔交易前需要的金额都比交换前的两笔交易所需的金额要大,因此更大的金额会出现在交换后的交易顺序中,即出现在满足bi<bi+1的交易顺序中。因此在交易顺序的前部分,如果发现前一笔交易的bi比后一笔交易的bi+1大,那么就交换这两笔交易。

  同理可证对于交易的后部分,即满足aibi<0的部分,更大的交易金额会出现在满足ai>ai+1的交易顺序中。

  因此最大的交易金额必定会出现在这样的交易顺序中:交易顺序满足aibi0在前部分,aibi<0的在后部分,在前部分中满足bi<bi+1,在后部分中满足ai>ai+1

  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

posted @   onlyblues  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示