数据结构·堆
堆
堆是一种数据结构。没了
堆是一种树形结构,堆顶始终保持为所有元素的最优值,所以常常运用于贪心中。
大根堆的根为堆的最大值,小根堆的根为堆的最小值。堆一般用二叉树实现。
【YbtOj】题解
A.合并果子
可以贪心地想到每次选取代价最小的两堆进行合并,再将新的一堆放入所有待选堆中。每次操作用小根堆维护最小代价即可,复杂度
关于
B.序列合并
用大根堆维护目前所选数的最大值,因为给出序列已具有单调性,所以直接for循环两个指针分别从前往后分别扫两个序列即可。若目前没选够
AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n;
int a[N],b[N];
priority_queue <int> q;
stack <int> s;
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
int t=a[i]+b[j];
if (q.size()<n) { q.push(t); continue; }
if (t<q.top()) { q.pop(); q.push(t); continue; }
break;
}
}
while (!q.empty()) { s.push(q.top()); q.pop(); }
while (!s.empty()) { printf("%lld ",s.top()); s.pop(); }
return 0;
}
C.龙珠游戏
看到神似“删除”的操作,想到链表。用大根堆维护目前队列的最大值,若当前堆顶
来自sxht dalao:不用堆优化就能做到
AC代码(带无意义的堆优化)
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int N=1e5+5;
int n;
int a[N];
int nxt[N],pre[N];
bool flag[N];
priority_queue <pii> q;
queue <int> ans;
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) { scanf("%lld",&a[i]); q.push(mp(a[i],i)); }
for (int i=1;i<n;i++) nxt[i]=i+1;
for (int i=2;i<=n;i++) pre[i]=i-1;
while (ans.size()<n)
{
while (!q.empty()&&(flag[q.top().second]||nxt[q.top().second]==0)) q.pop();
int t1=q.top().second,t2=nxt[t1];
flag[t1]=true,flag[t2]=true;
pre[nxt[t2]]=pre[t1],nxt[pre[t1]]=nxt[t2];
ans.push(a[t1]),ans.push(a[t2]);
q.pop();
}
while (!ans.empty()) { printf("%lld ",ans.front()); ans.pop(); }
return 0;
}
D.工作安排
很典的一道反悔贪心。为了能完成尽量多的工作,我们每次肯定是取截止时间更靠前的工作;但若是有一个工作靠前,但获利少,而后面存在一个获利更多的工作,这样的贪心选择就会不那么优。于是我们会想到,每次对于一个获利更多但没时间的工作,将已选的工作中获利最少的替换掉,这样一定是更优的。
所以,刚开始按照截止日期升序排序,用小根堆维护目前已选的最小的
AC代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=1e5+5;
int n;
struct node{
int d,p;
}a[N];
priority_queue < pii,vector <pii>,greater<pii> > q;
int ans;
bool cmp(node x,node y) { return x.d<y.d; }
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld%lld",&a[i].d,&a[i].p);
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
if (q.size()<a[i].d)
{
ans+=a[i].p;
q.push({a[i].p,a[i].d});
}
else
{
if (a[i].p>q.top().first)
{
ans=ans-q.top().first+a[i].p;
q.pop();
q.push({a[i].p,a[i].d});
}
}
}
printf("%lld",ans);
return 0;
}
E.家庭作业
也是很典的一道题呢(让我想起来了一个月前让我痛苦万分的分冰棍!)
先按照截止时间排序,每次尽量不喝奶茶,若对于当前
AC代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,double>
#define mp make_pair
using namespace std;
const int N=2e5+5;
int n;
struct node{
int l,t,d;
}a[N];
int tme;
double ans;
priority_queue < pii > q;
bool cmp(node x,node y) { return x.d<y.d; }
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i].l,&a[i].t,&a[i].d);
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
q.push(mp(a[i].l,1.0*a[i].t));//又多了一种奶茶
tme+=a[i].t;
while (tme>a[i].d)//还得喝奶茶
{
int fst=q.top().first,d=tme-a[i].d;
double scd=q.top().second;
q.pop();
if (scd>=d)//要补的
{
ans+=1.0*d/(1.0*fst);
if (scd!=d) q.push(mp(fst,scd-1.0*d));
tme=a[i].d;
break;
}
else//不够
{
tme-=scd;
ans+=scd/(1.0*fst);
}
}
}
printf("%.2lf",ans);
return 0;
}
F.选数游戏
若选了第
于是,从左到右遍历数列,遍历到第
到第
AC代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=1e6+5;
int n,m,k;
int a[N];
priority_queue < int,vector <int>,greater<int> > q;
int cnt,sum;
int ans;
signed main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for (int i=1;i<=m;i++) scanf("%lld",&a[i]);
int maxx=0;
for (int i=1;i<=min(m,k);i++)
{
q.push(a[i]);
cnt+=n,sum+=n*a[i];
while (cnt>k&&!q.empty())
{
cnt-=n-1,sum-=(n-1)*q.top();
maxx=q.top();
q.pop();
}
ans=max(ans,sum+min(n,k-cnt)*maxx);
}
printf("%lld",ans);
return 0;
}
G.内存管理
直接模拟即可。小根堆维护当前编号最小的未分配内存块,队列维护被分配的内存块顺序,
AC代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define mp make_pair
#define fst first
#define scd second
using namespace std;
const int N=3e4+5;
int t[N];//对于被占用的内存块i,最后一次被操作的时间
priority_queue < int,vector <int>,greater<int> > q;
queue <pii> out;
signed main()
{
for (int i=1;i<=30000;i++) q.push(i);
int x,y;
char c;
while (cin>>x>>c)
{
while (!out.empty()&&out.front().scd<=x-600)
{
if (out.front().scd==t[out.front().fst])
{
t[out.front().fst]=0;
q.push(out.front().fst);
}
out.pop();
}
if (c=='+')
{
cout<<q.top()<<'\n';//分配编号最小的空闲的
t[q.top()]=x;
out.push(mp(q.top(),x));
q.pop();
}
if (c=='.')
{
cin>>y;
if (t[y]) cout<<'+'<<'\n';
else cout<<'-'<<'\n';
if (t[y]) t[y]=x;
out.push(mp(y,x));
}
}
return 0;
}
H.火车载客
写了n次,果然没我想的那么简单啊啊啊
贪心考虑,一定是让第
将第
AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5;
int k,n,c;
struct train
{
int s,t,p,id;//p:还有多少人可上车 id:已上车多少人
bool operator < (const train &a) const { return t<a.t; }
}a[N];
struct node
{
int tme,id,op;
bool operator < (const node &a) const
{
if (tme!=a.tme) return tme<a.tme;
else return op>a.op;
}
}b[N<<1];
int tol;
priority_queue <train> q;
int ans,sum;
signed main()
{
scanf("%lld%lld%lld",&k,&n,&c);
for (int i=1;i<=k;i++)
{
scanf("%lld%lld%lld",&a[i].s,&a[i].t,&a[i].p);
b[++tol]={a[i].s,i,0};
b[++tol]={a[i].t,i,1};
}
sort(b+1,b+1+tol);
for (int i=1;i<=tol;i++)
{
int id=b[i].id;
if (b[i].op==1)
{
ans+=a[id].id ,sum-=a[id].id;
a[id].id=0;
continue;
}
//该下下
while (!q.empty()&&q.top().t<b[i].tme) { a[q.top().id].id=0 ; q.pop(); }
//尽量上
while (sum<c&&a[id].p)
{
int add=min(c-sum,a[id].p);
a[id].id+=add,a[id].p-=add;
q.push((train){a[id].s,a[id].t,add,id});
sum+=add;
}
//更优就上
while (!q.empty()&&a[id].p&&a[id]<q.top())
{
train tmp=q.top();
q.pop();
int add=min(a[id].p,tmp.p);
tmp.p-=add;
a[tmp.id].id-=add;
a[id].id+=add,a[id].p-=add;
if (tmp.p) q.push(tmp);
q.push((train){a[id].s,a[id].t,add,id});
}
}
printf("%lld",ans);
return 0;
}
I.矩阵选数
数据存在问题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】