第十五章:贪心进阶

本文介绍了贪心算法在解决带期限和惩罚的任务调度、区间问题、邻项交换等问题中的应用。通过具体的实例和算法分析,展示了如何通过贪心策略找到最优解,包括任务安排的优先级、区间覆盖和排序等关键步骤。
摘要由CSDN通过智能技术生成

贪心进阶

带期限和惩罚的任务调度问题

1. 补作业

小李刚刚参加完信息学竞赛,现在,他有很多作业要做。 每个老师都给了他交作业的最后期限。 如果小李在截止日期之后交作业,老师就会降低他最终考试的分数。 现在,我们假设每科作业都需要做一整天。 小李希望你帮助他安排家庭作业的顺序,以最大程度地减少分数的降低。

输入格式:

第1行:一个正整数N(1<=N<=1000),表示作业的数量。

第2行:N个空格分隔的整数,第i个整数d_i,表示第i个作业的截止日期为d_i(1<= d_i <= N)。

第3行:N个空格分隔的整数,第i个整数s_i表示第i份作业如果没有按时提交,将会被扣除s_i (1<=s_i <= 100000)。

输出格式:

1行:一个整数,表示小李可能被降低的分数的最小值。

输入输出样列

输入样例1:

3
3 3 3
10 5 1

Copy

输出样例1:

0

Copy

输入样例2:

3
1 3 1
6 2 3

Copy

输出样例2:

3

Copy

输入样例3:

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

Copy

输出样例3:

5

Copy

【算法分析】

贪心选择∶优先为惩罚最高的任务安排时间,并将处理时间安排的尽量靠后。

为什么要将处理时间安排的尽量靠后呢?

因为任务只要在截止时间结束之前完成即可,也就是说只要在截止时间之前安排1个时间点即可,越往后安排,越可以为截止时间早的任务预留时间。

完整的代码如下所示:

#include<bits/stdc++.h>
using namespace std;
struct hw{
    int d,s;   //d为截止时间,s为惩罚分数 
}; 
bool cmp(hw a,hw b)  //按照惩罚时间从大到小排序 
{
    if(a.s!=b.s)
    {
        return a.s>b.s; 
    }       
} 
int n,ans;
bool used[1010];  //标记时间是否已经被使用 
hw a[1010];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i].d;
    for(int i=1;i<=n;i++)
        cin>>a[i].s;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)  //枚举任务 
    {
        bool flag=0;   //是否安排成功 
        for(int j=a[i].d;j>=1;j--)  //按时间从后往前安排任务 
        {
            if(used[j]==0)  
            {
                flag=1;
                used[j]=1;
                break;
            }
        }
        if(flag==0) ans+=a[i].s; //安排失败,交罚款 
    }
    cout<<ans;
    return 0;
}

Copy

2. 智力大冲浪(主题库1540)

小李报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者m元。先不要太高兴!因为这些钱还不一定都是你的。接下来主持人宣布了比赛规则: 首先,比赛时间分为n个时段(n≤500),它又给出了很多小游戏,每个小游戏都必须在规定期限ti前完成(1≤ti≤n)。如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱wi,wi为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小李很想赢得冠军,当然更想赢取最多的钱! 注意:比赛绝对不会让参赛者赔钱!

输入格式

输入共4行。

第一行为m,表示一开始奖励给每位参赛者的钱;

第二行为n,表示有n个小游戏;n≤500

第三行有n个数,分别表示游戏1~n的规定完成期限;

第四行有n个数,分别表示游戏1~n不能在规定期限前完成的扣款数。

输出格式

仅1行。表示小李能赢取最多的钱。

输入输出样列

输入样例1:

10000
7
4 2 4 3 1 4 6
70 60 50 40 30 20 10

Copy

输出样例1:

9950

Copy

【算法分析】

仔细分析本题,不难发现,本题跟上面一题是完全相似的题目。首先根据扣款数从大到小排序,然后将截止时间尽可能的靠后,即:

第一组(4,70)放在第4个位置;

第二组(2,60)放在第2个位置;

第三组(4,50)由于第4个位置已经有人了,所以放在第3个位置;

第四组(3,40)由于第3根第2个位置已经有人了,所以放在第1个位置;

第五组(1,30)由于第1个位置已经有人了,所以放不下,因此扣除30元;

第六组(4,20)由于第4个位置以及之前的位置都已经有人了,所以放不下,因此扣除20元;

第七组(6,30)可以排在第6个位置。

综上,一共扣除了30+20元,还剩下9950元。

完整的代码如下所示:

#include<bits/stdc++.h>
using namespace std;
struct hw{
    int d,s;   //d为截止时间,s为惩罚分数 
}; 
bool cmp(hw a,hw b)  //按照惩罚时间从大到小排序 
{
    if(a.s!=b.s)
    {
        return a.s>b.s; 
    }       
} 
int m,n,ans;
bool used[1010];  //标记时间是否已经被使用 
hw a[1010];
int main()
{
    cin>>m; 
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i].d;
    for(int i=1;i<=n;i++)
        cin>>a[i].s;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)  //枚举任务 
    {
        bool flag=0;   //是否安排成功 
        for(int j=a[i].d;j>=1;j--)  //按时间从后往前安排任务 
        {
            if(used[j]==0)  
            {
                flag=1;
                used[j]=1;
                break;
            }
        }
        if(flag==0) ans+=a[i].s; //安排失败,交罚款 
    }
    cout<<m-ans;
    return 0;
}

Copy

区间

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值