反悔贪心复习笔记
讲解
本质上就是先贪心选择,遇到更优的再换。
P1484 种树
题目概述
给你 \(n\) 个位置,每个位置上有权值 \(a_i\),你至多选择 \(m\) 个位置两两不相邻,求你得到的最大权值。
分析
首先设 \(l_i,r_i\) 表示旁边两个位置。
用大根堆存值和下标。
如果我当前选择了 \((val,i)\)(如果 \(val\leq 0\) 就直接退出,因为是至多选 \(m\) 个),按照下面的步骤进行:
- 让答案 \(ans+val\)。
- 让 \(a_i=a_{l_i}+a_{r_i}-a_i\),作为反悔,因为下一次如果选到这个值,就说明我不要 \(i\),而要另外的两个。
- 将 \((a_i,i)\) 放入队列中(记得删除队列中标记不能选择的值)。
- 执行
del(i)函数- 标记 \(l_i,r_i\) 不可选择,令 \(l_i=l_{l_i},r_i=r_{r_i}\),因为下一次选择这个值的时候不可选的就是另外两个的了(可以看作把他们三个合并成一个了)。
- 令 \(l_{r_i}=i,r_{l_i}=i\)。
为什么可以合并呢?
假设 \(5\) 个数 \([a,b,c,d,e]\),你先选择了 \(c\),然后将 \([b,c,d]\) 合并成 \(b+d-c\),下一次答案加这个值的时候就少了 \(c\)。
所以序列变成了 \([a,b+d-c,e]\),你又选择了 \(b+d-c\),那么所以将 \([a,b+d-c,e]\) 合并成 \(a+e-b-d+c\),是不是中间的那个又选上了,而且这个选的次数是恰好递增的。
因为是合并,所以我们的标记数组是不会变的。
代码
时间复杂度 \(\mathcal{O}(m\log n)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define PII pair<int,int>
#define N 300005
using namespace std;
int n,k,a[N],l[N],r[N];
bool vis[N];
priority_queue<PII> q;
void del(int id) {
vis[l[id]] = vis[r[id]] = 1;
l[id] = l[l[id]];
r[id] = r[r[id]];
r[l[id]] = id;
l[r[id]] = id;
}
signed main(){
cin >> n >> k;
for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]),l[i] = i - 1,r[i] = i + 1;
for (int i = 1;i <= n;i ++) q.push({a[i],i});
int ans = 0;
for (int i = 1;i <= k;i ++) {
while(vis[q.top().second]) q.pop();
auto t = q.top();
q.pop();
if (t.first <= 0) break;
ans += t.first;
a[t.second] = a[l[t.second]] + a[r[t.second]] - a[t.second];
q.push({a[t.second],t.second});
del(t.second);
}
cout << ans;
return 0;
}
P1792 [国家集训队] 种树
题目概述
更上一题差不多,只不过要求了必须要 \(m\) 个,且序列是一个环。
分析
同理。
代码
时间复杂度 \(\mathcal{O}(m\log n)\)。
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define N 200005
#define PII pair<int,int>
using namespace std;
int n,m,a[N],l[N],r[N];
bool vis[N];
priority_queue<PII> q;
void del(int id) {
vis[l[id]] = vis[r[id]] = 1;
l[id] = l[l[id]],r[id] = r[r[id]];
r[l[id]] = id,l[r[id]] = id;
}
signed main(){
cin >> n >> m;
if (m > n / 2) return cout << "Error!",0;
for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]),l[i] = i - 1,r[i] = i + 1,q.push({a[i],i});
r[n] = 1;
l[1] = n;
int ans = 0;
for (int i = 1;i <= m;i ++) {
while(vis[q.top().second]) q.pop();
auto t = q.top();
q.pop();
ans += t.first;
a[t.second] = a[l[t.second]] + a[r[t.second]] - a[t.second];
q.push({a[t.second],t.second});
del(t.second);
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号