洛谷Oj-P1315 观光公交-贪心

问题描述:
风景迷人的小城Y 市,拥有n 个美丽的景点。由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第 0 分钟出现在 1号景点,随后依次前往 2、3 、4 ……n 号景点。从第 i 号景点开到第 i+1 号景点需要 Di 分钟。任意时刻,公交车只能往前开,或在景点处等待。
设共有m 个游客,每位游客需要乘车1 次从一个景点到达另一个景点,第i 位游客在Ti 分钟来到景点 Ai ,希望乘车前往景点Bi (Ai

struct P
{
    int t;//到达出发车站的时间
    int a;//出发地
    int b;//目标地
    int arr;//到达目标车站的时间
};
P p[10010];
int d[1010];//从i到i+1所花费的时间
int sum_peo[1010];//乘车区间[i,i+1]的乘客数
int arr_time[1010];//从景点i去往别处景点的最晚到达车站的人到达的时间
bool cmp(const P &a,const P &b)//按出发地从小到大排序,出发地相同就按到达出发地的时间从小到大排序
{
    if(a.a == b.a)
        return a.t < b.t;
    return a.a < b.a;
}
struct Ans
{
    int cnt_peo;//车上的人数
    int cnt_k;//最多使用的加速器的个数
};
vector<Ans> v;
bool compare(const Ans &a,const Ans &b)//比较函数之二
{
    return a.cnt_peo > b.cnt_peo;//按人数从大到小排序
}
int main()
{
    int n,m,k;
    cin >> n >> m >> k;//输入景点数、乘客数和加速器数
    for(int i = 1; i <= n - 1; ++i)//输入耗时
        cin >> d[i];
    for(int i = 1; i <= m; ++i)//输入乘客信息
        cin >> p[i].t >> p[i].a >> p[i].b;
    sort(p + 1,p + m + 1,cmp);//按照出发点由小到大排序
    for(int i = 1; i <= m; ++i)//对于每一位乘客
        arr_time[p[i].a] = p[i].t;//迭代
    for(int i = 1; i <= m; ++i)//对于每一位乘客
        for(int j = p[i].a; j <= p[i].b - 1; ++j)
            sum_peo[j]++;
    //以下部分就是一个模拟
    int cur_time = 0;
    for(int i = 1; i <= n - 1; ++i)//对于每一个可以作为出发点的景点(不使用加速器)
    {
        //乘客齐了就发车,乘客不齐就等待几分钟
        cur_time += max(0,arr_time[i] - cur_time);
        //发车喽
        cur_time += d[i];//加上观光车路上花费的时间(这是观光车到达下一站的时间)
        for(int j = 1; j <= m; ++j)//为到达目的地的乘客赋上到达的时间
            if(p[j].b == i + 1)//注意是下一站
                p[j].arr = cur_time;
//      核心代码
//      乘车区间[i,i+1]中乘客的人数
//      到达下一站的时候人齐了,就有必要加速
//      到达下一站的时候人还不齐,就没有必要加速
//      就算加了速还是要等,对答案没有贡献
        if(i == n - 1)
            v.push_back({sum_peo[i],d[i]});
        else
            v.push_back({sum_peo[i],max(0,cur_time - arr_time[i + 1])});
    }
    //经过对拍(就拍了一组数据),计算不用加速器所花费时间的代码没有问题
    //以下代码忽视了对之后上车的人的影响的分类讨论
    int cost_time = 0;
    for(int i = 1; i <= m; ++i)
        cost_time += p[i].arr - p[i].t;
    sort(v.begin(),v.end(),compare);
    for(int i = 0; i <= v.size() - 1; ++i)
    {
        if(k >= v[i].cnt_k)
        {
            k -= v[i].cnt_k;
            cost_time -= v[i].cnt_k * v[i].cnt_peo;
        }
        else
        {
            cost_time -= k * v[i].cnt_peo;
            break;
        }
    }
    cout << cost_time << endl;
    return 0;
}

AC代码:

int n,m,k,ans;
int bus[1010];//bus[i]为观光车到达第i个景点的时间(绝对量)
int d[1010];//d[i]为从第i个景点到第i+1个景点路上所花的时间
int arr_time[1010];//arr_time[i]为最晚到第i个景点的乘客到达的时间
int get_off[1010];//get_off[i]为在第i个景点下车的人数
int sum[1010];
void init()
{
    cin >> n >> m >> k;//景点数、乘客数、加速器数
    for(int i = 1; i <= n - 1; ++i)
        cin >> d[i];//路程时间
    for(int i = 1; i <= m; ++i)
    {
        int t,a,b;
        cin >> t >> a >> b;
        ans -= t;//由于所花时间的总和等于到达的时间减去来到的时间再求和,这里先把来到的时间减去,之后再加上到达的时间
        arr_time[a] = max(arr_time[a],t);
        get_off[b]++;
    }
    return;
}
void solve()
{
    while(k--)//对于每一个加速器。不断贪心,不断迭代。因为前一次贪可能会影响下一次贪心
    {
        for(int i = 1; i <= n; ++i)//公式
            //观光车到达第i个顶点的时间等于从第i-1个景点发车的时间加上路上所花的时间
            bus[i] = max(bus[i - 1],arr_time[i - 1]) + d[i - 1];
        for(int i = n; i >= 2; --i)//倒着推受益乘客的人数
        {
            if(d[i-1] == 0)//如果加速器使得从第i-1个景点到第i个景点的时间变为了0,则[i - 1,i]的受益乘客为0
                sum[i - 1] = 0;
            else//若不为0
            {
                sum[i - 1] = get_off[i];//[i - 1,i]受益乘客的人数就等于在第i个景点下车的人数
                if(bus[i] > arr_time[i])//如果观光车到达第i个景点的时候人已经到齐了
                    sum[i - 1] += sum[i];//[i - 1,i]受益的乘客的人数要加上[i,i + 1]的,否则,就卡住了
            }
        }
        int max_sum = 0;是0而不是-inf
        int j;
        for(int i = 1; i <= n - 1; ++i)//找出受益乘客最多的那一段
            if(sum[i] > max_sum)//起码要大于0
            {
                max_sum = sum[i];
                j = i;
            }
        if(max_sum == 0)//如果无法继续使用加速器
            break;//跳出循环
        else
            d[j]--;//否则就减去路程
    }
    for(int i = 1; i <= n; ++i)
            bus[i]= max(bus[i - 1],arr_time[i - 1]) + d[i-1];//再递推一遍
    for(int i = 1; i <= n; ++i)
        ans += bus[i] * get_off[i];//加上到达第i个景点的时间与在第i个景点下车的人数
    return;
}
int main()
{
    init();
    solve();
    cout << ans << endl;
    return 0;
}

心路历程:
经过分析可以知道加速器最好在车上乘客比较多的时候使用,这样对于减少旅行时间总和的贡献比较大。但是早早地到达下一站但是乘客还没到齐也是不能出发,白白地浪费时间和加速器,所以使用加速器也要有个度。综合以上两种因素,既要考虑此时此刻乘客的人数,又要考虑不能加速过头,如何分配加速器还是有点难度啊
有以下的因素需要考虑
①从第i个景点到第i+1个景点的观光车上有几位乘客
②最晚到达第i个景点的乘客将于第几分钟到达

先把总的时间加起来,再减去加速器的贡献

观光车有早到、准点到和晚到这三种情况。如果早到了的话,就只能在那里等着了。准点到不在话下。如果晚到的话,就要使用加速器使得观光车准点到。可是此时是否有使用加速器的必要呢?我们说还要考虑车上的人数。不妨这样:把此时车上的人数和使用的加速器个数上限(多用无益)给出,利用贪心的思想排序即可
需要注意的是,如果某车站没有乘客上车,则arr_time为0。

如果我是司机,当前的时间为cur。等乘客到齐后(①已经到齐cur>=arr_time,立即发车②还没到齐cur < arr_time,需要在原地进行等待arr_time - cur分钟)从第i个景点出发–>cur加上在路上花费的时间得出到达下一站的时间(下车的乘客请带好随身物品准备下车)。继续重复以上过程。
以上是我的分析,可惜除了模拟当司机的那块,其它的都有问题
答案=求和(到达目的地时间-到达出发点时间)
我们发现到达出发点时间是不可更改的,所以尽量让更多的人早早地到达目的地是贪心的关键
假如我们对[i,i+1]这条路线上使用了加速器,在[i,i+1]中的乘客都会受到影响,即早到第i+1个景点。那对之后的乘客会不会有影响呢,以下分两种情况,车到了第i+1个景点时最后一个人还没有到。那么从第i+1个景点出发且已经到达的人依然要等待,这一部分不能早到。车到了第i+1个景点时人已经到齐了,那么拉上人就走,所有人都能早到。这个分析是比以上我的分析更加深入的,不错。
到达该景点的时间等于到达该景点前一个景点的时间加上到达该景点的路的时间,或者是到达该景点的前一个景点的最晚上车的人的时间加上到达该景点的路的时间
!a与a == 0是等价的
真的是佩服写出这个程序的人

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章