1.先把用户按照年龄大小排序,因为后面给出的是年龄范围,所以需要快速找出符合年龄要求的用户
2.建立一个年龄数组,该数组记录了以年龄x为左界时,用户数组的idx,以年龄x为右界时,用户数组的idx,通过年龄数组,就可以快速定位到用户数组的对应位置
3.刚开始,在查询输入时,直接定位到用户数组,把对应的用户数组段复制出来,进行排序,但是会超时(卡了还挺久的),然后仔细思考,发现用户数组的数量可能很大,<=100000,查询数量最大为2000,假如每次都复制100000,然后再进行排序,会严重浪费时间。
4.再观察题目,发现最终需要显示的数量最多只有100个,于是考虑建立一个小根堆,把它的大小维持在需要显示的数量showSum,最后复制输出即可。
为什么建立小根堆?我们的目的是要找出财富值最大的showSum个用户,建立小根堆,堆顶为showSum个用户中财富最小的,假如遍历用户j,发现用户j的财富值比小根堆的堆顶要大,那么把用户压入小根堆,再从小根堆中弹出堆顶即可。
AC代码:
//#include<string>
//#include<stack>
//#include<unordered_set>
//#include <sstream>
//#include "func.h"
//#include <list>
#include <iomanip>
#include<unordered_map>
#include<set>
#include<queue>
#include<map>
#include<vector>
#include <algorithm>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
#include<stack>
using namespace std;
struct PeopleNode{
int age, netWorth;
char name[9];
PeopleNode() :age(-1), netWorth(-1){};
};
bool cmp(const PeopleNode&a, const PeopleNode&b)
{//根据年龄排序
if (a.age < b.age)
return true;
else
return false;
}
struct cmpHeap{//根据题目的显示规则排序
bool operator()(const PeopleNode&a, const PeopleNode&b)
{
if (a.netWorth > b.netWorth)
return true;
else if (a.netWorth == b.netWorth && a.age < b.age)
return true;
else if (a.netWorth == b.netWorth && a.age == b.age)
{
int asize = 0, bsize = 0;
for (int i = 0; i < 9; i++)
{
if (a.name[i] != 0)
asize++;
else
break;
}
for (int i = 0; i < 9; i++)
{
if (b.name[i] != 0)
bsize++;
else
break;
}
for (int i = 0; i < min(asize, bsize); i++)
{
if (a.name[i] < b.name[i])
return true;
else if (a.name[i] > b.name[i])
return false;
}
if (asize < bsize) return true;
else return false;
}
else
return false;
}
};
int main(void)
{
int peopleSum, querySum;
scanf("%d %d", &peopleSum, &querySum);
vector<PeopleNode> people(peopleSum);
vector<vector<PeopleNode>> agePeople(201);
for (int i = 0; i < peopleSum; i++)
{
scanf("%s %d %d", people[i].name, &people[i].age, &people[i].netWorth);
}
sort(people.begin(), people.end(), cmp);
vector<pair<int, int>> ageRange(201, { -1, -1 });
int idx = 0;
//计算年龄范围数组
for (int i = 0; i < ageRange.size(); i++)
{
if (idx < people.size())//确保idx在size内
{
if (idx == 0 && people[idx].age > i)
{//idx不进行++,而i++,直到i和age相等
ageRange[i].first = 0;
ageRange[i].second = -1;//以-1作为右边界,意思是不存在年龄小于等于i的人
}
else if (idx != 0 && people[idx].age > i)
{//idx不进行++,而i++,直到i和age相等
ageRange[i].first = idx;//i岁的搜索的起始idx,以idx作为左边界
ageRange[i].second = idx - 1;//i岁的搜索的终止idx,以idx-1作为右边界
}
else if (people[idx].age == i)
{//如果i等于年龄
ageRange[i].first = idx;
while (idx < people.size() && people[idx].age == i)
{//一直到age和i不相等
idx++;
}
ageRange[i].second = idx - 1;//第idx个人的年龄不等于i,第idx-1个人的年龄等于i
}
}
else if (i>0)
{//如果i的值大于0,且idx超出范围(即已经遍历完用户)
ageRange[i].first = people.size();//把i的左界设置为无效值
ageRange[i].second = ageRange[i - 1].second;//把i的右界设置为用户idx的最大值
}
}
for (int i = 0; i < querySum; i++)
{
int showSum, minA, maxA;
scanf("%d %d %d", &showSum, &minA, &maxA);
int sum = ageRange[maxA].second - ageRange[minA].first + 1;//求出以minA为左界,maxA为右界的people数量
printf("Case #%d:\n", i + 1);
if (sum > 0)
{//存在用户,sum的数量可能很大,用户取值<=100000,但是需要显示的数量<=100,所以用一个小根堆来维护需要显示的人
vector<PeopleNode>::iterator ite = people.begin();
priority_queue<PeopleNode, vector<PeopleNode>, cmpHeap> q;
for (int j = 0; j < sum; j++)
{
if (q.size() == showSum && q.top().netWorth <= (ite + j + ageRange[minA].first)->netWorth)
{//如果小根堆的数量已经达到显示数量,但是小根堆的顶部财富小于等于当前遍历的财富值,则压入
q.push(*((ite + j + ageRange[minA].first)));
q.pop();//相应地,弹出一个
}
else if (q.size()<showSum)//如果小根堆还没满,直接压入即可
q.push(*((ite + j + ageRange[minA].first)));
}
vector<PeopleNode> show(min(showSum, (int)q.size()));
for (int j = show.size() - 1; j >= 0; j--)
{//因为需要升序排列,用的是小根堆,所以把小根堆的堆顶放在数组尾部
show[j] = q.top();
q.pop();
}
for (int j = 0; j < show.size(); j++)
{
printf("%s %d %d\n", show[j].name, show[j].age, show[j].netWorth);
}
}
else
{//数量小于等于0,直接输出None
printf("None\n");
sum = 0;
}
}
return 0;
}