贪心算法——活动选择

题目描述

假设有一个需要使用某一资源的活动组成的集合S,S={1,……n}.(n<1000)该资源一次只能被一个活动占用,每一个活动有一个开始时间bi和一个结束事件ei(bi <=ei).若bi >=ej或者bj >=ei,则活动i和活动j兼容。 你的任务是:选择由相互兼容的活动组成的最大集合。 输入:

输入

输入共n+1行,其中第1行为n,第2行到第n+1行表示n个活动的开始时间和结束事件。(中间用空格隔开。)格式为 n b1 e1 …… bn en

输出

输出共两行,第一行为满足要求的活动所占用的总时间t。(可以理解为选择的活动中最后一个活动的结束时间) 第二行为最大集合中的活动序号。每个数据见用一个空格隔开。

这道题可以用动态规划和贪心做,我介绍的算法为贪心

假设有两个区间x,y,区间x完全包含y,那么显然x和y之中只能取一个,且取y比较划算。这就是此题的贪心策略,我们先按结束时间把每个事件排序,现在如果有任何一个事件的开始时间小于前面任何一个事件的开始时间,那么这个事件就会被舍弃。经过这些操作以后,这道题就变成了一个简单的区间合并,直接逐个判断后放入。

具体见以下代码和每一个细节的注释

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct nnn//运用结构体的目的是方便交换活动序号--f
{
    int f;int a;int b;
}s[1005];
int comp(nnn x,nnn y)
{
    return x.b<y.b;//根据结束时间从小到大排序
}
int n,m=-1,ml,ans,an[1000];
bool f[10000];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&s[i].a,&s[i].b);
        s[i].f=i;//记录序号
    }
    sort(s+1,s+n+1,comp);
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
            if(s[i].a>s[j].a)//舍弃s[j]类(不如s[i]类)
            {
                for(int k=j;k<=n;k++)
                    s[k]=s[k+1];
                n--;
            }
    for(int i=1;i<=n;i++)
    {
        f[0]=0;//让f[0]成为一个单独的flag
        for(int j=s[i].a;j<s[i].b;j++)//注意留出结束时间,比如1点到3点是不包括3点的
            if(f[j])
                f[0]=1;
        if(!f[0])//判断此区间是否有点被覆盖
        {
            an[++ml]=s[i].f;
            for(int j=s[i].a;j<s[i].b;j++)
                f[j]=1;//此区间所有点标记
            m=max(m,s[i].b);//记录最大结束时间
        }
    }
    printf("%d\n",m);
    sort(an+1,an+ml+1);//从小到大排序后输出
    for(int i=1;i<ml;i++)
        printf("%d ",an[i]);
    printf("%d",an[ml]);
}


猜你喜欢

转载自blog.csdn.net/qq_37657307/article/details/77573865