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$, $money \geq {cost}_i$ must hold true. After performing a transaction, money becomes $money - {cost}_i + {cashback}_i$.
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:
$1 \leq transactions.length \leq {10}^{5}$
$transactions[i].length == 2$
$0 \leq {cost}_i, {cashback}_i \leq {10}^{9}$
解题思路
对于某种顺序确定后如何求最大值呢,假设已经完成了前$i-1$笔交易,那么第$i$笔的交易应该要满足$$\mathrm{money} - (a_0 - b_0) - (a_1 - b_1) - \cdots - (a_{i-1} - b_{i-1}) \geq a_i$$即$$\mathrm{money} \geq (a_0 - b_0) + (a_1 - b_1) + \cdots + (a_{i-1} + b_{i-1}) + a_i$$
$\mathrm{money}$要取最小值,那么就要遍历所有情况,取不等式右边的最大值。那么最大值会出现在哪一种情况呢,从集合的角度来看,根据$a_i$的不同把方案分成若干个集合,不等式右边的最大值一定会在其中一个集合出现。接着枚举$a_i$,当$a_i$固定后,要使得右式取最大值等价于求$(a_0 - b_0) + (a_1 - b_1) + \cdots + (a_{i-1} + b_{i-1})$的最大值,当每一项都是正数时,即$a_k - b_k > 0$,就可以取到最大值。因此一开始可以枚举 transactions 数组,把$a_k - b_k > 0$的交易累加,就可以取到最大值,然后枚举$a_i$,如果有$a_i - b_i > 0$那么总和减去$a_i - b_i$再加上$a_i$就可以快速算出不等式右边的值。
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大佬提供的思路。
先说一下算法的执行过程,首先根据$a_i - b_i$的正负性分成两组,其中对于$a_i - b_i \geq 0$这组的元素按照$b_i$进行升序排序,对于$a_i - b_i < 0$这组的元素按照$a_i$进行降序排序。然后对这两组进行合并,其中$a_i - b_i \geq 0$的在前部分,$a_i - b_i < 0$的在后部分。接下来枚举每一个元素,用$r$来表示上一次交易结束后剩余的金额,对于某个交易$i$,如果有$r < a_i$,意味着初始时至少要再补$a_i - r$这么多的金额,同时需要把$r$更新成$a_i$,然后更新$r = r - a_i + b_i$。最后返回答案(也就是每次交易前要补的金额的总和)。
下面来证明这种做法是对的。
要使得能完成所有交易的最少钱数最大,意味着每次交易前所需的金额应尽可能的大,即在第$i$笔交易前所需的金额$\sum\limits_{k=1}^{i-1}{(a_k-b_k)}+a_i$应尽可能的大。那么是否存在一种交易顺序,使得在某笔交易前的$\sum\limits_{k=1}^{i-1}{(a_k-b_k)}+a_i$能取到最大值呢?
在所有交易顺序构成的集合中,我们先考虑其中一个子集,这个子集内的交易顺序都满足$a_i - b_i \geq 0$在前部分,$a_i - b_i < 0$的在后部分,现在要证明能完成所有交易的最少钱数最大值必定在这个子集中。假设在某个交易顺序中,第$i$笔交易满足$a_i - b_i < 0$,第$i + 1$笔交易满足$a_{i+1} - b_{i+1} \geq 0$。现在来看看交换第$i$笔和第$i+1$笔交易前后所需的金额大小,需要注意的是,交换后只会对进行第$i$笔和第$i+1$笔交易前所需的金额数产生影响,其他笔交易的所需金额不会发生改变。
进行第$i$笔交易前需要的金额 | 进行第$i+1$笔交易前需要的金额 | |
交换前 | $\sum\limits_{k=1}^{i-1}{(a_k-b_k)}+a_i$ | $\sum\limits_{k=1}^{i}{(a_k-b_k)}+a_{i+1}$ |
交换后 | $\sum\limits_{k=1}^{i-1}{(a_k-b_k)}+a_{i+1}$ | $\sum\limits_{k=1}^{i-1}{(a_k-b_k)}+a_{i+1}-b_{i+1}+a_i$ |
化简,对每个式子减去$\sum\limits_{k=1}^{i-1}{(a_k-b_k)}$,得到:
进行第$i$笔交易前需要的金额 | 进行第$i+1$笔交易前需要的金额 | |
交换前 | $a_i$ | $a_i - b_i + a_{i+1}$ |
交换后 | $a_{i+1}$ | $a_{i+1} - b_{i+1} + a_{i}$ |
可以发现,由于$a_i - b_i < 0$,因此有$a_{i+1} > a_i - b_i + a_{i+1}$;由于$a_i - b_i \geq 0$,因此有$a_{i+1} - b_{i+1} + a_{i} \geq a_i$,即交换后的两笔交易所需的金额比交换前的要大,因此更大的金额会出现在交换后的交易顺序中。因此对于某个交易顺序,只要发现前一笔交易的$a_i - b_i < 0$,而后一笔交易$a_{i+1} - b_{i+1} \geq 0$,那么我们就交换这两笔交易的顺序,这样总是可以把交易的顺序变成$a_i - b_i \geq 0$在前部分,$a_i - b_i < 0$的在后部分,且所需的交易金额会变大。
现在我们把答案的范围缩小到这个子集中,再去看看在这个子集中,答案是否存在于某个更小的子集中。
先考虑交易顺序的前部分,即满足$a_i - b_i \geq 0$的部分。假设某个交易顺序中第$i$笔交易和第$i+1$笔交易满足$b_i > b_{i+1}$,现在来看看交换第$i$笔和第$i+1$笔交易前后所需的金额大小(与上面得到的结果一样):
进行第$i$笔交易前需要的金额 | 进行第$i+1$笔交易前需要的金额 | |
交换前 | $a_i$ | $a_i - b_i + a_{i+1}$ |
交换后 | $a_{i+1}$ | $a_{i+1} - b_{i+1} + a_{i}$ |
由于此时$a_{i+1} - b_{i+1} \geq 0$,因此$a_{i+1} - b_{i+1} + a_{i} \geq a_i$,同时又因为$b_i > b_{i+1}$,因此$a_{i+1} - b_{i+1} + a_{i} > a_i - b_i + a_{i+1}$,即交换后的第$i+1$笔交易前需要的金额都比交换前的两笔交易所需的金额要大,因此更大的金额会出现在交换后的交易顺序中,即出现在满足$b_i < b_{i+1}$的交易顺序中。因此在交易顺序的前部分,如果发现前一笔交易的$b_i$比后一笔交易的$b_{i+1}$大,那么就交换这两笔交易。
同理可证对于交易的后部分,即满足$a_i - b_i < 0$的部分,更大的交易金额会出现在满足$a_i > a_{i+1}$的交易顺序中。
因此最大的交易金额必定会出现在这样的交易顺序中:交易顺序满足$a_i - b_i \geq 0$在前部分,$a_i - b_i < 0$的在后部分,在前部分中满足$b_i < b_{i+1}$,在后部分中满足$a_i > a_{i+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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16708819.html