【习题总结】反悔贪心
前面的话:
遗物。
反悔贪心
SoyTony
教育我们要写反悔贪心
SoyTony
瑞平:
“什么叫
SoyTony
教育你要做反悔贪心?SoyTony
教育你这个?就当是教育了吧。"
反悔贪心题单
模型:
至少这些题里只有两个模型:
- 一种靠链表维护节点间的相邻关系,限制是相邻的两点只能选一个。
- 另一种靠若干个堆维护反悔机制,限制是一个点有多个不同的属性,只能选择一种属性产生贡献。
反悔机制:
模型一:
模型一的限制在于位置关系,比如相邻的点不能选,可以想到用双向链表来维护点之间的位置关系。由于选最大的点不一定最优,它两边的点加起来可能更优。所以当我们取一个点后,再加入一个点,这个点的权值是左边的点权 \(+\) 右边的点权 \(-\) 自身的点权。
模型二:
模型二的限制在于同一个点只有一个属性能产生贡献。可以用若干个堆来维护这些属性的转移关系,再相关的分类讨论,进行转移。
题:
P1484 种树
典型的模型一,两个相邻的坑不会都种树,用链表维护坑与坑之间的相邻关系,用大根堆维护。注意边界条件。
Code
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int MAXN = 5e5 + 10;
const int INF = 998244353;
int n, k;
LL sum, ans;
bool vis[MAXN];
struct List{
int last, next;
}l[MAXN];
struct Place{
LL val, pos;
bool operator < (const Place &b) const{
return val < b.val;
}
}p[MAXN];
priority_queue< Place, vector<Place>, less<Place> > q;
inline LL read(){
LL x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int main(){
n = read(), k = read();
for(register int i = 1; i <= n; i++){
p[i].val = read(), p[i].pos = i;
q.push(p[i]);
}
p[0] = (Place){-INF, 0};
p[n + 1] = (Place){-INF, n + 1};
q.push(p[0]), q.push(p[n + 1]);
l[0].next = 1, l[n + 1].last = n;
for(register int i = 1; i <= n; i++)
l[i].last = i - 1, l[i].next = i + 1;
for(register int i = 1; i <= k; i++){
while(!q.empty() && vis[q.top().pos]) q.pop();
Place t = q.top();
q.pop();
sum += t.val;
int u = l[t.pos].last, v = l[t.pos].next;
vis[u] = vis[v] = true;
p[t.pos].val = p[u].val + p[v].val - t.val;
l[t.pos].last = l[u].last, l[t.pos].next = l[v].next;
l[l[u].last].next = t.pos, l[l[v].next].last = t.pos;
q.push(p[t.pos]);
ans = max(ans, sum);
}
printf("%lld", ans);
return 0;
}
P3620 [APIO/CTSC2007] 数据备份
显然,让两个相邻的大楼相连是最优的,每栋大楼只能连一根电缆,典型的返回贪心,依然是链表维护相邻关系,小根堆维护。
点击查看代码
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int MAXN = 1e5 + 10;
const int INF = 2147483647;
int n, k;
LL ans;
LL sit[MAXN];
bool vis[MAXN];
struct List{
int last, next;
}l[MAXN];
struct Line{
LL len, pos;
bool operator > (const Line &b) const{
return len > b.len;
}
}b[MAXN];
priority_queue< Line, vector<Line>, greater<Line> > q;
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int main(){
n = read(), k = read();
for(register int i = 1; i <= n; i++)
sit[i] = read();
b[0] = (Line){INF, 0};
b[n] = (Line){INF, n};
q.push(b[0]), q.push(b[n]);
for(register int i = 1; i < n; i++){
int st = sit[i], ed = sit[i + 1];
b[i].len = ed - st, b[i].pos = i;
q.push(b[i]);
}
for(register int i = 1; i < n; i++)
l[i].last = i - 1, l[i].next = i + 1;
for(register int i = 1; i <= k; i++){
while(!q.empty() && vis[q.top().pos]) q.pop();
Line t = q.top();
q.pop();
ans += t.len;
int u = l[t.pos].last, v = l[t.pos].next;
vis[u] = vis[v] = true;
b[t.pos].len = b[u].len + b[v].len - t.len;
l[t.pos].last = l[u].last, l[t.pos].next = l[v].next;
l[l[u].last].next = t.pos, l[l[v].next].last = t.pos;
q.push(b[t.pos]);
}
printf("%lld", ans);
return 0;
}
CF730I Olympiad in Programming and Sports
套用模型二,我们先把前 \(p\) 大的丢进编程团队,剩下的有两种情况:
- 将一个没选过的人加入运动团队,价值 \(b_i\)。
- 将一个选过的人加入运动团队,再找一个人加入编程团队,价值 \(b_i - a_i + a_j\)。
开三个堆维护即可。
Code
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 3010;
int n, p, s, sum;
int bel[MAXN];
struct Student{
int cs, pe, cut, id;
}a[MAXN];
struct Cmp1{
bool operator () (const Student &a, const Student &b) const{
return a.cs < b.cs;
}
};
struct Cmp2{
bool operator () (const Student &a, const Student &b) const{
return a.pe < b.pe;
}
};
struct Cmp3{
bool operator () (const Student &a, const Student &b) const{
return a.cut < b.cut;
}
};
priority_queue< Student, vector< Student >, Cmp1 > q1;
priority_queue< Student, vector< Student >, Cmp2 > q2;
priority_queue< Student, vector< Student >, Cmp3 > q3;
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int main(){
n = read(), p = read(), s = read();
for(register int i = 1; i <= n; i++) a[i].cs = read();
for(register int i = 1; i <= n; i++) a[i].pe = read();
for(register int i = 1; i <= n; i++){
a[i].id = i, a[i].cut = a[i].pe - a[i].cs;
q1.push(a[i]);
}
for(register int i = 1; i <= p; i++){
Student t = q1.top(); q1.pop();
sum += t.cs, bel[t.id] = 1;
}
for(register int i = 1; i <= n; i++){
if(!bel[i]) q2.push(a[i]);
else q3.push(a[i]);
}
for(register int i = 1; i <= s; i++){
while(!q1.empty() && bel[q1.top().id]) q1.pop();
while(!q2.empty() && bel[q2.top().id]) q2.pop();
Student t1 = q1.top(); q1.pop();
Student t2 = q2.top(); q2.pop();
Student t3 = q3.top(); q3.pop();
if(t3.cut + t1.cs > t2.pe){
sum += t3.cut + t1.cs;
bel[t1.id] = 1, bel[t3.id] = 2;
q2.push(t2), q3.push(a[t1.id]);
}
else{
sum += t2.pe;
bel[t2.id] = 2;
q1.push(t1), q3.push(t3);
}
}
printf("%d\n", sum);
for(register int i = 1; i <= n; i++) if(bel[i] == 1) printf("%d ", i);
puts("");
for(register int i = 1; i <= n; i++) if(bel[i] == 2) printf("%d ", i);
return 0;
}
以下为博客签名,与博文无关。
只要你们不停下来,那前面就一定有我。所以啊,不要停下来~
本文来自博客园,作者:TSTYFST,转载请注明原文链接:https://www.cnblogs.com/TSTYFST/p/16920719.html