SRM481D1 500 (Greedy + Math)

一晃好多年,我终于爬回了D1,可是今天这场不怎么好过……NA LIB的无线果然不好使……断了两次……
其实250没做出来也不怪它……逻辑性好强……我没想清楚……唉……不早了,明天再想……

唯一值得庆幸的是最后过了500,虽然比赛规定时间内没完成,不过思路差不多对了,只是算法细节上要注意好多……

500是道概率题

问题是给你一条流水线,有n个job要做,但只有一台机器,每次只能做一个job。每个job都有需要的时间以及请求这个job的人。这个系统很聪明,他会schedule一套方案来让所有请求job的人(注意是人)等的时间的平均值最少。如果有几种方案,取任意一种。求的却是每个job完成时间的期望。

好了,首先我们要知道的是,这个机器是怎么schedule的。如果没有“人”这个条件的限制,很明显就是一个greedy,先做时间最少的。但问题这里是minimize人的平均等待时间。如果还是照之前的greedy,就出问题了。题目的Case1就是例子。稍微推一下就可以发现,schedule的方法是把每个人的job都合并起来,对总时间排序,先把一个人的做完了,再做下一个人的。但一个人的工作中间的次序是任意的,谁前谁后这就是要算期望了。推出这个期望跟杨辉三角很有关系~嗯~但是还没完,如果几个人的工作总时间相同,他们之间的顺序也任意,这又是一层期望。所以要算两层,外面这层还是跟杨辉三角有关……推出公式就发现很简单了,剩下的就是仔细code了。

code的时候因为时间最大是10^9,所以int超了,fail一次……改long long,有两个小地方没改过来,又fail一次,最后过的很顺利。印象中第一次做出D1 500,再接再厉。

贴code:

#include "cmath"
#include "ctime"
#include "iostream"
#include "string"
#include "vector"
#include "map"
#include "cstring"
#include "cstdio"
#include "set"
#include "algorithm"
#include "queue"
using namespace std;
typedef long long LL;
LL t[55];
 
bool cmp(const int i,const int j)
{
    return t[i]<t[j];
}
 
class BatchSystemRoulette {
public:
    vector <double> expectedFinishTimes( vector <int> duration, vector <string> user ) {
        int n=duration.size();
        map<string,int> m;
        vector<int> v[55];
        int id[55];
        vector<double> ret;
        memset(t,0,sizeof(t));
        int nu=0;
        for(int i=0;i<n;i++)
            if(m.find(user[i])==m.end()) { m[user[i]]=nu++; }
        for(int i=0;i<n;i++)
        {
            ret.push_back(0);
            t[m[user[i]]]+=duration[i];
            v[m[user[i]]].push_back(i);
        }
        for(int i=0;i<nu;i++) id[i]=i;
        sort(id,id+nu,cmp);
        for(int i=0;i<nu;i++)
        {
            int p=v[id[i]].size();
            for(int j=0;j<p;j++)
            {
                ret[v[id[i]][j]]=duration[v[id[i]][j]]+(t[id[i]]-duration[v[id[i]][j]])/2.0;
            }
        }
        LL now=0,pre=t[id[0]],cnt=1;
        for(int i=1;i<=nu;i++)
        {
            if(i==nu||t[id[i]]!=pre) {
                for(int j=i-1;j>=i-cnt;j--)
                {
                    int p=v[id[j]].size();
                    double tmp=(now*2.0+(cnt-1)*pre)/2.0;
                    for(int k=0;k<p;k++)
                    ret[v[id[j]][k]]+=tmp;
                }
                if(i!=nu)
                {
                    now+=cnt*pre;
                    cnt=1; pre=t[id[i]];
                }
            }
            else cnt++;
        }
        return ret;
    }
};