题目描述
题目描述
为了找到自己满意的工作,牛牛收集了每种工作的难度和报酬。牛牛选工作的标准是在难度不超过自身能力值的情况下,牛牛选择报酬最高的工作。在牛牛选定了自己的工作后,牛牛的小伙伴们来找牛牛帮忙选工作,牛牛依然使用自己的标准来帮助小伙伴们。牛牛的小伙伴太多了,于是他只好把这个任务交给了你。
输入描述:
每个输入包含一个测试用例。
每个测试用例的第一行包含两个正整数,分别表示工作的数量N(N<=100000)和小伙伴的数量M(M<=100000)。
接下来的N行每行包含两个正整数,分别表示该项工作的难度Di(Di<=1000000000)和报酬Pi(Pi<=1000000000)。
接下来的一行包含M个正整数,分别表示M个小伙伴的能力值Ai(Ai<=1000000000)。
保证不存在两项工作的报酬相同。
输出描述:
对于每个小伙伴,在单独的一行输出一个正整数表示他能得到的最高报酬。一个工作可以被多个人选择。
时间限制:2秒 空间限制:65536K
题目分析
我们可以看到,这道题并不难,可以暴力破解,时间复杂度为O(n*m)。但是由于其数据量过大,有超时的风险。因此我们必须优化其时间复杂度,将其降低到O(mlogn),甚至是O(n+m)。
解决方案
首先,给出下文的符号及其释义。
符号 | 释义 |
---|---|
n | 工作的数量 |
m | 小伙伴的数量 |
di[i] | 第i个工作的难度 |
pi[i] | 第i个工作的报酬 |
ai[i] | 第i个小伙伴的能力 |
pay[i] | 第i个小伙伴的最高报酬 |
下面给出的是暴力破解的核心代码。
int ai,pay;
for(int i=0;i<m;++i)
{
cin>>ai;
pay=0;
for(int j=0;j<n;j++)
{
if(ai>=di[j]&&pay>pi[j])
pay=pi[j];
}
cout<<pay<<'\n';
}
分析以上代码可以得知,暴力破解的时间复杂度过高是由于需要在混乱的数组中查找符合能力要求且工资最高的的工作。所以,优化其查找算法可以有效降低其时间复杂度。
自然而然,我们可以先剔除报酬与难度不匹配的工作(即剔除有其他工作难度低于其难度,但报酬不低于其报酬的工作)。然后,可以用快排将di[]和pi[]以di为key排序,然后查找时使用二分查找即可。此时的时间复杂度为max(O(nlogn),O(mlongn))。其中,nlogn为将di与pi快排的时间复杂度。mlogn为给m个小伙伴用快排寻找最高工作报酬的时间复杂度
下面给出核心代码
priority_queue<map> work;
...
clear(work);
for(int i=0;i<m;++i)
{
pay=bisearch(work,ai[i]);
}
既然能将di与pi排序,那么我们可以考虑是否能将ai排序呢?
答案是肯定的的。将ai与i绑定,以免混淆小伙伴,然后以ai为key快排即可。输出时,可以依次将其队首比较即可。下面给出核心代码。
priority_queue<map> human;
...
while(!human.empty())
{
...
if(work.empty())
{
while(!human.empty())
{
pay[human.top()->value]=0;
human.pop();
}
break;
}
if(human.top()->key>=work.top()->key)
{
pay[human.top()->value]=work.top()->value;
human.pop();
}
else
{
while(work.top()->key>human.top()->key&&!work.empty())
{
work.pop();
}
}
}
此时,时间复杂度为max(O(nlogn),O(mlogm),O(n+m))。
2019/9/15 补充:
或许有人要问,将小伙伴排序,时间复杂度没有优化,那么这有什么用?实际上,若用空间换时间的策略的话,可以用计数排序代替快排。计数排序的时间复杂度为O(n),那么时间复杂度会被降为O(n+m)。
明天的题
题目描述
小Q得到一个神奇的数列: 1, 12, 123,…12345678910,1234567891011…。
并且小Q对于能否被3整除这个性质很感兴趣。
小Q现在希望你能帮他计算一下从数列的第l个到第r个(包含端点)有多少个数可以被3整除。
若有更好的想法,欢迎讨论区见。