题意:
就是给你n个压力,然后有m个人可以去承担,每个人只能承担一个,现在问你如何去分配这m个人。使得这m个人的压力方差最小。
思考:
刚开始我还以为是压力的方差最小,害,区域赛的题目确实都很难懂。然后题目保证了m>=n,刚开始由于我读错了,所以直接对于每个压力排序后,从后面开始尽量让当前值和va[1]一样,题意错了就不说了。那么既然保证了m>=n,很明显每个压力可以先分配一个人,然后剩下的人怎么分配?,发现对于每个压力对答案的贡献就是(当前的压力-所有人压力的平均值)✖这个压力的人数。由于平均压力是定值。所以用一个单调队列,结构体的排序按,如果这个压力增加一个人,可以让方差更小,那么就给他加一个人,就这样贪心m-n即可。最后遍历对列,把每个压力对答案的贡献算上,最后别忘记了除以m,方差的公式。
值得注意的是,虽然给了3秒,但是队列的取出和放入也是很占时间的,所以这题还卡cin和ll,所以尽量用scanf和int吧。
代码:
int T,n,m,k;
db va[N];
db sum,ans;
struct node{
int a,b; //那个压力,有多少人
db c; //平均每个人的压力
friend operator < (node A,node B)
{
db t1 = (A.c-sum)*(A.c-sum)*A.b; //不加人对答案的贡献
db t2 = (va[A.a]/(A.b+1)-sum)*(va[A.a]/(A.b+1)-sum)*(A.b+1); //加人对答案的贡献
db t3 = (B.c-sum)*(B.c-sum)*B.b;
db t4 = (va[B.a]/(B.b+1)-sum)*(va[B.a]/(B.b+1)-sum)*(B.b+1);
db x1 = t1-t2,x2 = t3-t4; //尽量让差大的去加人
return x1<x2;
}
};
signed main()
{
IOS;
scanf("%d",&T);
priority_queue<node> q;
for(int cs=1;cs<=T;cs++)
{
sum = ans = 0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lf",&va[i]);
for(int i=1;i<=n;i++) sum += va[i];
sum /= m; //m个人的压力平均值
for(int i=1;i<=n;i++) q.push({i,1,va[i]});
int last = m-n;
while(last--)
{
auto now = q.top();q.pop();
q.push({now.a,now.b+1,va[now.a]/(now.b+1)}); //让最优的去加人
}
while(q.size())
{
auto now = q.top();q.pop();
ans += (now.c-sum)*(now.c-sum)*now.b; //贡献*人数
}
ans /= m;
printf("Case #%d: %.8lf\n",cs,ans);
}
return 0;
}
总结:
多多积累经验呀,多多思考,队列是个贪心的好方法。