智力大冲浪的数据加强版,n^2算法要被卡。
我们发现,原来的暴力代码最暴力的是这一段:
for (register int j=num[i].pos; j>=1; --j)
if (!f[j])
{
f[j]=true,ans+=num[i].w;
break;
}
那么对于一个个往前找,直到找到一个空格子的这个步骤,能不能把它变成找1次就找到呢?
显然是不行的…但是log次,或a(n),即ackermaan,是可以的,所以就用一个并查集来维护每个格子前面第一个空格子是哪个格子。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,xx,yy,ans;
int f[N];
struct number{int pos,w;}num[N];
inline bool cmp(number a,number b)
{
return a.w>b.w;
}
int find(int x)
{
if (f[x]==x) return x;
return f[x]=find(f[x]);
}
int main(){
scanf("%d",&n);
for (register int i=1; i<=700000; ++i) f[i]=i;
for (register int i=1; i<=n; ++i) scanf("%d%d",&num[i].pos,&num[i].w);
sort(num+1,num+n+1,cmp);
for (register int i=1; i<=n; ++i)
{
xx=find(num[i].pos);
if (xx)
{
ans+=num[i].w;
yy=find(xx-1);
f[xx]=yy;
}
}
printf("%d\n",ans);
return 0;
}
还有一种思路,用一个大根堆来维护。
上一个代码可以理解为数据结构优化,而大根堆维护的思路就完全不同了。
从后往前进行扫描,如果发现有若干个pos为当前日期作业,就把它们的w放进大根堆中,然后每天可以完成一项作业,即每天可以从大根堆中取出堆顶元素,然后答案进行累加。如果在某些天堆中无元素了,即是题中的作业浪费情况了。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,now,ans;
int f[N];
struct number{int pos,w;}num[N];
inline bool cmp(number a,number b)
{
return a.pos<b.pos;
}
priority_queue<int>q;
int main(){
scanf("%d",&n);
for (register int i=1; i<=n; ++i) scanf("%d%d",&num[i].pos,&num[i].w);
sort(num+1,num+n+1,cmp);
now=n;
for (register int i=700000; i>=1; --i)
{
while (now && num[now].pos>=i) q.push(num[now].w),now--;
if (!q.size()) continue;
ans+=q.top(); q.pop();
}
printf("%d\n",ans);
return 0;
}