CSP_Week4 Problem A DDL困境(贪心板子c++)

题目概述

原题叙述

ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。
所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。

INPUT

输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。
每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。
然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。

OUTPUT

对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。

输入样例

3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4

输出样例

0
3
5

备注

上方有三组样例。
对于第一组样例,有三个作业它们的DDL均为第三天,ZJM每天做一个正好在DDL前全部做完,所以没有扣分,输出0。
对于第二组样例,有三个作业,它们的DDL分别为第一天,第三天、第一天。ZJM在第一天做了第一个作业,第二天做了第二个作业,共扣了3分,输出3。

题目重述

有n个DDL,他们都有一个固定的结束时间和分值,只要在结束时间之前完成就可以获得相应的分数。但与传统的分值贪心问题不同的是,完成每个DDL需要的时间是相同的都为1天。
由于输入比较复杂进行一下说明方便后面的解题:
第一行:要处理的数据样例个数
第二行:第一组样例的数据个数
第三行:每个DDL的结束的时间
第四行:每个DDL的分值
后面部分就是第二行到第四行的循环(样例说明见题目备注)

解题思路

题目比较明显就是一道贪心算法的题目。但是要注意的有下面几个点:
1、DDL在结束的那一天完成是可以获得积分的。
2、完成每个DDL的花费是相同的(一天),但是收获并不相同(不同积分)。
3、题目要求的是求出最少的扣分,也可以理解为得到最多的分数。
根据上面的三个点,我们就可以开始建立贪心策略了,由于是等花费,不用再考虑时间段带来的问题,只需要尽可能安排分数较高的DDL。为了保证在安排时对其他DDL的影响最小,我们采用倒叙安排从最后一天开始安排能够安排的DDL中分值最高的。
有趣的是,虽然题面简单,该题目通过改变数据量的大小也能一定程度上加大难度:
可以从下面两个角度思考问题:
如果数据区域是1e3即1000,选择什么实现方法?
如果数据区域增加到1e6 ,前面的方法还会奏效么?又该使用什么算法呢?
很明显1e3的数据范围,O(n^2)的时间复杂度是完全可以接受的。我们可以暴力的进行求解,也就是首先按照分值排序,然后在每次的选择时都遍历一遍DDL,选择当前可以安排且没有被安排过的,且分值最大的进行安排。这种算法虽然笨重,但是数据较少且想不到好的优化方法时,不失为一种备用的方案。
接下来是Problem 2,数据量提升到了1e6,经过简单计算可以知道O(n^2)的时间复杂度一定会爆掉。在不重构的算法的前提下,我们可以在前面算法的基础上进行优化。经过分析可知,Problem 1的算法浪费了大量的时间复杂度在选DDL的过程中。但实际上,我们的实质需求只是选出最大分数的DDL。在选最大值的算法优化方面,堆是一种简便且高效的方法,引入大根堆即可将时间复杂度将至O(logn),完成题目需求。

题目源码

这里我们只列出了Problem 1的解法,Problem 2等待你的补充。

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
struct ddl
{
    int time;
    int score;
    int flag;
    bool operator<(ddl & a)
    {
        return score>a.score;
    }
}a[1010];
int main()
{
    int number=0;
    scanf("%d",&number);
    for(int i=0;i<number;i++)
    {
        int all_score=0;
        int end_time=0;
        int choose_score=0; 
        int group=0;
        scanf("%d",&group);
        for(int j=0;j<group;j++)
        {
            a[j].flag=0;
            a[j].score=0;
            a[j].time=0;
            scanf("%d",&a[j].time);
            if(a[j].time>end_time)
            end_time=a[j].time;
        }
        for(int j=0;j<group;j++)
        {
            scanf("%d",&a[j].score);
            all_score+=a[j].score;
        }
        sort(a,a+group);
        for(int k=end_time;k>=1;k--)
        {
            for(int q=0;q<group;q++)
            {
                if(a[q].flag==0 && a[q].time>=k)
                {
                    a[q].flag=1;
                    choose_score+=a[q].score;
                    break;
                }
            }
        }
        printf("%d\n",all_score-choose_score);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值