题目意思
T组案例,给一个n,然后给n个数字,给出n个数字,从第一个点走到最后一个点,可以在任意一个点以ai的价格买或者卖物品,求最大利润和最大利润下的最少次数。
Sample Input
3
4
1 2 10 9
5
9 5 9 10 5
2
2 1
Sample Output
16 4
5 2
0 0
Hint
In the first case, he will buy in 1, 2 and resell in 3, 4. profit = - 1 - 2 + 10 + 9 = 16
In the second case, he will buy in 2 and resell in 4. profit = - 5 + 10 = 5
In the third case, he will do nothing and earn nothing. profit = 0
解题思路
CF原题???只不过加了一个次数???2018CCPC网络赛???
赛后才知道CF原题,赛时队友说先全买在卖,然后我觉得应该先找最大然后找最大的左边的最小的,然而讨论来讨论去,总觉得有问题,然后看到我们学校有队伍过03了,跟榜去了,赛后发现挺简单的,一发AC???
维护一个multiset,如果multiset中有比现在的数字小的数字就买卖一次,这里就有一个问题了,就是买卖次数可能会有重复,这时,我们需要记录一下数字出现的次数(num),数字第一次出现的次数(book),如果当前的买卖的买值 t 满足num[t]==book[t]
,说明该数字都是第一次出现的,即cnt++,book[t]--
,反之不进行任何操作。
一个易错点,买次进行买卖操作时,对于每一个买值,需要insert两次,因为需要让其有传递性,而且在当前位置我也可以买入,不妨以4个数字1 2 3 4为例,当遍历完后,若insert一次,set中则只有4一个元素,ans=3,cnt=2
,若insert两次,set中有1 2 两个元素,ans=4,cnt=4
,显然需要inset两次,让其具有传递性。
AC代码
#include <iostream>
#include <set>
#include <map>
#define ll long long
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
multiset<int>s;
map<int, int>book;
map<int, int>num;
int n, t;
ll ans = 0, cnt = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &t);
if (s.size() == 0 || *s.begin() >= t)
{
s.insert(t);
num[t]++;
book[t]++;
}
else
{
if (book[*s.begin()] == num[*s.begin()])
{
cnt++;
book[*s.begin()]--;
}
num[*s.begin()]--;
ans += t - *s.begin();
s.erase(s.begin());
s.insert(t);
s.insert(t);
num[t] += 2;
book[t]++;
}
}
cnt *= 2;
printf("%lld %lld\n", ans, cnt);
}
}