cf437E(贪心+优先队列)

本文探讨了在已知未来股票价格的情况下,如何通过精确的买卖策略实现最大利润。文章提出了一种利用两个堆的数据结构来维护买入和卖出股票的价格,从而快速找到最佳交易对,实现利益最大化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:知道每天股票价格,可以在任意一天购买或出售一个股票,求最大获利

这个可以这样考虑,把这些股票分成2部分,买的和卖的。。然后对一个股票的决策,有2种情况可以操作,一种是直接从买的股票中选最小的和他配对。那这样就会产生一些问题,这个股票的最佳配对可能不是最小的,可能是买这个股票。那么就有第二种操作,就是把一个卖的股票变成买的股票,然后用当前的股票来代替他,这样就可以化解上面的情况了。。

然后需要用2个堆来维护这2堆股票,从2个堆中选最小的股票去进行处理就行了。。如果这个股票都没有这2堆股票大,显然是要放进买的那堆。。最后对于没有和他匹配的买的股票,就可以不进行任何操作了。。

 

 

 

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神兽保佑,代码无bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1LL<<(x))
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid ((x+y)/2)
#define NM 300005
#define nm 2005
#define pi 3.1415926535897931
const int inf=1e9+7;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}







int n,a[NM];
ll ans;
priority_queue<int,vector<int>,greater<int> >q,t;

int main(){
    n=read();inc(i,1,n)a[i]=read();
    q.push(a[1]);
    inc(i,2,n){
	if(t.empty()||(!q.empty()&&q.top()<t.top())){
	    if(q.top()<a[i]){
		t.push(a[i]);ans+=a[i]-q.top();q.pop();
	    }else q.push(a[i]);
	}else{
	    if(t.top()<a[i])q.push(t.top()),t.push(a[i]),ans+=a[i]-t.top(),t.pop();
	    else q.push(a[i]);
	}
    }
    return 0*printf("%lld\n",ans);
}

 

 

 

 

 

E. Buy Low Sell High

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

You can perfectly predict the price of a certain stock for the next N days. You would like to profit on this knowledge, but only want to transact one share of stock per day. That is, each day you will either buy one share, sell one share, or do nothing. Initially you own zero shares, and you cannot sell shares when you don't own any. At the end of the N days you would like to again own zero shares, but want to have as much money as possible.

Input

Input begins with an integer N (2 ≤ N ≤ 3·105), the number of days.

Following this is a line with exactly N integers p1, p2, ..., pN (1 ≤ pi ≤ 106). The price of one share of stock on the i-th day is given by pi.

Output

Print the maximum amount of money you can end up with at the end of N days.

Examples

Input

Copy

9
10 5 4 7 9 12 6 2 10

Output

Copy

20

Input

Copy

20
3 1 4 1 5 9 2 6 5 3 5 8 9 7 9 3 2 3 8 4

Output

Copy

41

Note

In the first example, buy a share at 5, buy another at 4, sell one at 9 and another at 12. Then buy at 2 and sell at 10. The total profit is  - 5 - 4 + 9 + 12 - 2 + 10 = 20.

 

<think>我们讨论的是CF1506D "Epic Transformation"问题。该问题要求计算在数组中最多可以执行多少次移除两个不同元素的操作。解决方案的核心是统计元素频率,然后根据最大频率与总元素数的关系计算最大操作次数。 解决方案公式: 最大操作次数 = min(n // 2, n - max_frequency) 其中,max_frequency是数组中出现次数最多的元素的频率。 现在,我们提供C++代码实现。注意,由于n最大为200,000,我们需要高效地统计频率。在C++中,我们可以使用unordered_map来统计频率,然后遍历map找到最大频率。 代码步骤: 1. 读取测试用例数t。 2. 对于每个测试用例: a. 读取整数n。 b. 读取n个整数组成的数组。 c. 使用unordered_map统计每个元素的频率。 d. 遍历map,找到最大频率max_freq。 e. 计算操作次数:ans = min(n/2, n - max_freq) 3. 输出ans。 注意:n/2是整数除法,向下取整,所以用n/2即可(在整数运算中,n/2就是整除)。 但是注意:当n为奇数时,n/2会向下取整,例如n=5,则n/2=2(因为整数除法)。所以直接使用n/2(整数除法)即可。 代码示例:</think>### CF1506D "Epic Transformation" 的 C++ 解决方案 #### 问题核心思路 给定数组 $a$,每次移除两个不同元素,求最大操作次数。解决方案基于贪心策略: 1. 统计元素频率,找到最大频率 $f_{\text{max}}$ 2. 最大操作次数 = $\min\left(\lfloor n/2 \rfloor, n - f_{\text{max}}\right)$ - 当 $f_{\text{max}} \leq n/2$ 时,所有元素可充分配对 - 否则,操作次数受限于非高频元素数量 #### C++ 代码实现 ```cpp #include <iostream> #include <vector> #include <unordered_map> #include <algorithm> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n; cin >> n; vector<int> arr(n); unordered_map<int, int> freq; int max_freq = 0; // 统计频率并记录最大值 for (int i = 0; i < n; i++) { cin >> arr[i]; freq[arr[i]]++; max_freq = max(max_freq, freq[arr[i]]); } // 核心公式计算 int ans = min(n / 2, n - max_freq); cout << ans << "\n"; } return 0; } ``` #### 代码说明 1. **输入处理**: - `ios::sync_with_stdio(false); cin.tie(0);` 加速输入输出 - 读取测试用例数 $t$,循环处理每个用例 2. **频率统计**: - 使用 `unordered_map` 高效统计元素频率($O(1)$ 插入/更新) - 遍历数组时动态更新最大频率 $f_{\text{max}}$ 3. **核心计算**: - `ans = min(n / 2, n - max_freq)` 实现贪心策略 - `n / 2` 是整数除法(自动向下取整) 4. **时间复杂度**: - 统计频率:$O(n)$ - 总体复杂度:$O(n)$,满足 $n \leq 2 \times 10^5$ 的约束[^1] #### 测试示例 | 输入数组 | $n$ | $f_{\text{max}}$ | 计算过程 | 输出 | |-------------------|-----|------------------|------------------------|------| | `[3, 1, 2, 3]` | 4 | 2 (`3`出现2次) | $\min(4/2, 4-2) = 2$ | 2 | | `[1, 1, 1, 2]` | 4 | 3 (`1`出现3次) | $\min(4/2, 4-3) = 1$ | 1 | | `[1, 1, 1]` | 3 | 3 (`1`出现3次) | $\min(3/2, 3-3) = 0$ | 0 | | `[1, 2, 3, 4]` | 4 | 1 (所有频率为1) | $\min(4/2, 4-1) = 2$ | 2 | #### 算法正确性证明 - **情况1**:$f_{\text{max}} \leq \lfloor n/2 \rfloor$ 所有元素可配对,操作次数达到上界 $\lfloor n/2 \rfloor$,如 `[1,2,3,4]`。 - **情况2**:$f_{\text{max}} > \lfloor n/2 \rfloor$ 高频元素会阻塞配对,每次操作消耗一个高频元素和一个非高频元素,最大操作次数为 $n - f_{\text{max}}$,如 `[1,1,1,2]`[^3]。 ### 相关问题 1. **贪心策略证明** 如何严格证明该贪心策略的最优性?能否用反证法或数学归纳法证明剩余元素数 $\max(0, 2f_{\text{max}} - n)$ 不可再减少?[^3] 2. **操作规则扩展** 若每次操作可移除三个不同元素(如引用[^5]的负载均衡思想),最大操作次数公式如何变化?是否仍存在类似 $\min(\lfloor n/3 \rfloor, n - f_{\text{max}})$ 的关系? 3. **动态规划解法** 能否用动态规划解决此问题?状态如何定义?(参考引用[^4]的树形DP思想) 4. **实际应用扩展** 在内存管理场景中(如引用[^1]的士兵排布),如何将此算法用于优化不同大小内存块的分配/释放操作?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值