题解:2018级算法第三次上机 C3-Zexal的浩瀚星辰

题目描述:

 

样例:

 

实现解释:

一道结合了火箭发射的贪心题目

知识点:

贪心,优先队列

 

题目分析:

根据题目描述可知,延迟后时间是正常推进的,也就是假设共有n个火箭,推迟k小时。则在到达k+1小时时,每过一个小时只要火箭没发射完都会有k(如果k大于n就是有剩余数量)个火箭会遭受延迟的损失,显然这是必然的(因为到达k小时前的损失都已经确定了,无法改变)。

那么依据题意只要使得每次这k个火箭的损失最小即可,而如何最小:让其中单位时间损失最大的火箭发射即可,这样一定比发射其他火箭的损失要小。

于是便可得出贪心的状态转移方程:

cost[i] = cost[i-1]+sumsum既是此时除去最大损失火箭的总损失量)

sum的获取可以利用排序实现,不过考虑到时间问题,还是用优先队列进行最好,边输入边处理便可解决,具体实现可参考完整代码,内含注释。

 

难点:

即如何获得当前状态的sum,常规来说只需去除损失最大的火箭然后遍历剩余的相加即可。一次优化:利用sum数组和out变量提前存储避免遍历,不过还是需要排序;二次优化:利用优先队列获取最大值,免去排序。

 

完整代码:

优先队列版(过了):

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
//考虑到数据范围,这里需要用long long进行储存
//表面上看起来没有超范围,但是延迟时间是需要相乘的,因此还是会超过 
priority_queue<long long> pq;
long long sum[500010];
long long cost[1000010];
int main()
{
    ios::sync_with_stdio(false);
    int n,k,temp;
    long long p;//中间值记录每个火箭的损失 
    long long out;//统计发射出去火箭的损失和 
    while(cin >> n >> k)
    {
        memset(cost,0,(n+k+1)*sizeof(long long));
        //按需初始化,减少时间消耗 
        sum[0] = 0;//sum需要用到前一个值,此处设0
        out = 0;
        for(int i = 1;i<=k+n;i++)
        {
            if(i <= n)
            {
                cin >> p;
                sum[i] = sum[i-1]+p;
                pq.push(p);
            }
//错误判断1:if(i <= k) if(i <= n) cost[k]+=(k-i+1)*p;
//这种情况第二个else会被编译器认为是i <= n的补集,会出错 

//错误判断2:if(i <= k&&i <= n) cost[k]+=(k-i+1)*p;
//这种情况会导致i > k但i <= n的情况计算被忽略
//即延迟时间小于最后一个发射时间时 

            if(i <= k)//还没到延迟时间时依据p计算延迟时间时这个火箭的损失 
            {
                if(i <= n) cost[k]+=(k-i+1)*p;
                //注意火箭只有n个,所以需要判断下 
            }
            else//此时说明延迟已过,需要发射火箭 
            {
                temp = i>n?n:i;
                //当i>n则应该temp=n以保证每次获取到的为损失的总和
                //这样减去已经发射火箭的损失和就是这次发射的总损失 
                out += pq.top();//选择单位损失最高的火箭发射 
                pq.pop();
                cost[i] = cost[i-1] + sum[temp] - out;
                //i小时的损失等于i-1小时损失加上这一小时的新损失
                //当前时间本应发射的总损失减去已经发射的总损失即是延迟火箭新产生的总损失
                
                //out去除形式,本质相同,只是直接在sum处去除(影响也可保留) 
                //不过没有out好理解 
//                temp = i>n?n:i;
//                sum[temp] -= pq.top();
//                pq.pop();
//                cost[i] = cost[i-1] + sum[temp]; 
            }
        }
        cout << cost[k+n] << '\n';
        //延迟时间和总发射时间之和即是所有火箭发射完全的时间 
    }
    return 0;
}
View Code

 数组排序版(TLE):

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
long long p[500010];
long long sum[500010];
//long long cost[1000010];
int main()
{
    ios::sync_with_stdio(false);
    int n,k;
    int temp;
    long long out;
    while(cin >> n >> k)
    {
        long long cost[n+k+1];
        memset(cost,0,sizeof(cost));
        sum[0] = 0;
        for(int i = 1;i<=n;i++)
        {
            cin >> p[i];
            sum[i] = sum[i-1]+p[i];
            if(i <= k) cost[k]+=(k-i+1)*p[i];
        }
        out = 0;
        for(int i = 1;i<=n;i++)
        {
            temp = k+i;
            if(temp > n) temp = n;
            sort(p+1,p+temp+1);
            out += p[temp];
            p[temp] = 0;
            cost[k+i] = cost[k+i-1] + sum[temp] - out;
            //此时out不能省略了,因为sum已经确定了
        }
        cout << cost[k+n] << '\n';
    }
    return 0;
}
View Code

 

  

posted @ 2019-11-01 23:12  稼軒  阅读(253)  评论(0编辑  收藏  举报