【习题总结】反悔贪心
前面的话:
遗物。
反悔贪心
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
套用模型二,我们先把前 大的丢进编程团队,剩下的有两种情况:
- 将一个没选过的人加入运动团队,价值 。
- 将一个选过的人加入运动团队,再找一个人加入编程团队,价值 。
开三个堆维护即可。
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理