1. 程式人生 > >Scapegoat(2017ECFinal B)

Scapegoat(2017ECFinal B)

題目連結:

Scapegoat

 

題意:

有n個問題,有m個人來背鍋,每個問題至少要有一人背鍋。每個問題有權值a[i],若k個人同時為第i個問題背鍋,那麼每個人的壓力是a[i]/k。現在讓你分配幾個人為第幾個問題背鍋,使得每個人壓力的方差最小。

 

方差定義:

 

思路:

因為每個問題至少要有一人背鍋,所以只要考慮剩下的m-n個人即可。因為要讓方差儘可能小,所以我們需要讓每次一個人產生的貢獻儘可能大。所以每次讓一個人為他加入前後方差之差最大的那個問題背鍋就是最優的。

 

code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 2e5+10;

typedef struct node{
    int id;
    double cnt;
    double val;
    bool operator < (const node &num) const {
        if(val==num.val){
            return cnt<num.cnt;
        }
        return val<num.val;
    }
}Point;

int n,m;
double a[MAX];
double avg;
priority_queue<Point>q;
int mp[MAX];

int main()
{
    int T;
    scanf("%d",&T);
    int Case=1;
    while(T--)
    {
        while(!q.empty())   q.pop();
        scanf("%d%d",&n,&m);
        avg=0;
        for(int i=0;i<n;i++){
            scanf("%lf",&a[i]);
            mp[i]=1;
            avg+=a[i];
        }
        avg/=(double)m;
        for(int i=0;i<n;i++){
            double x = mp[i]*(a[i]/mp[i]-avg)*(a[i]/mp[i]-avg)-(mp[i]+1)*(a[i]/(mp[i]+1)-avg)*(a[i]/(mp[i]+1)-avg);
            q.push(Point{i,mp[i],x});
        }
        for(int i=0;i<m-n;i++){
            Point now = q.top();
            q.pop();
            mp[now.id]++;
            double x = mp[now.id]*(a[now.id]/mp[now.id]-avg)*(a[now.id]/mp[now.id]-avg)-(mp[now.id]+1)*(a[now.id]/(mp[now.id]+1)-avg)*(a[now.id]/(mp[now.id]+1)-avg);
            q.push(Point{now.id,mp[now.id],x});
        }
        double ans=0;
        for(int i=0;i<n;i++){
            ans+=1.0*mp[i]*(a[i]/mp[i]-avg)*(a[i]/mp[i]-avg);
        }
        ans/=(double)m;
        printf("Case #%d: %.12lf\n",Case++,ans);
    }
    return 0;
}