读一遍就有思路了:贪心原则是:从大到小(排序也是贪心很重要的一环)的敌人战力,我们肯定可以解决的oier会变多,对于没一种敌人战力,我们要对可以处理的oier进行平均分配使得经验值被均分。修补骑士一开始想的是逐步减少之后选择最小者来不断+1模拟处理。不过这样存在两个问题。1:追踪过程难以维护,可能要写优先队列之类,实现上会有问题。2:对于备选者全部平均的状态,我们新元素要给哪个或者那些元素呢?
这个事例告诉我们:不要老是想着追踪全状态,对于这道题而言,这里的敌人数量与可处理者数量都是不断上升的,我们每一次更新可以直接统计并进行计算:直接(mustcut + snum - 1) / snum;对每一种新加入的敌人战力都全部“从0开始”统计数量并取平均即可。
或许这里会想:那万一相对战力较大的敌人数量较多,直接取平均得到的状态不是正确的最佳状态因为对于战力较小的oier可能无法被正确平分。但这就是这道题最巧妙的地方:对于这种“高战力的较多”的情况,其对应的正确的可能均分一定在前面被统计过! 后面加入新oier与战力更低的新敌人。如果存在“高战力的无法被低战力的均分清除”的情况——这种情况在战力更小的敌人被加入统计之前一定以及被统计过!对最终的答案不会有影响(这也就是我们为什么使用max,可能更新的情况只有可能更低战斗力的敌人太多新加入即使是平均值也能比起原来的ans增大)
这里需要注意的一点事我们输入的战力可能会出现重复的情况,这里需要使用一点点额外的开销来实现对于所有同样战力敌人的相加
对于每一种战力情况,由于敌人战力我们是从大到小统计,直接用sum走过去就可以了,至于可以处理的oier人数的数量,可以使用lower_bound来快速找到大于当前战力值的oier的编号进而统计数量(记得先排序)
AC代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin>>n;
vector<int> mynum(n,0);
for(int r = 0;r < n;r++)
{
cin>>mynum[r];
}
int m;
cin>>m;
unordered_map<int,int> pownum;
vector<int> powidx;
vector<int> remit(m,0);
unordered_set<int> findit;
for(int r = 0;r < m;r++)
{
int temp;
cin>>temp;
remit[r] = temp;
if(findit.find(temp) == findit.end())
{
powidx.push_back(temp);
findit.insert(temp);
}
}
for(int r = 0;r < m;r++)
{
int temp;
cin>>temp;
pownum[remit[r]] += temp;//重复?
}
if(*max_element(mynum.begin(),mynum.end()) < *max_element(powidx.begin(),powidx.end()))
{
cout<<"-1"<<endl;
return 0;
}
sort(powidx.begin(),powidx.end());
reverse(powidx.begin(),powidx.end());
sort(mynum.begin(),mynum.end());
int ans = 0;
int mustcut = 0;
for(int idx : powidx)
{
mustcut += pownum[idx];
int snum = n - (lower_bound(mynum.begin(),mynum.end(),idx) - mynum.begin());
int dd = (mustcut + snum - 1) / snum;
ans = max(ans,dd);
}
cout<<ans<<endl;
return 0;
}