TOJ 5929 贪心+(并查集 || 优先队列)

5929: 家庭作业

时间限制(普通/Java):6000MS/18000MS 内存限制:65536KByte
总提交: 223 测试通过:44
描述

老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为10,要求在6天内交,那么要想拿到这10学分,就必须在第6天结束前交。

每个作业的完成时间都是只有一天。例如,假设有7次作业的学分和完成时间如下:

作业号 1 2 3 4 5 6 7
期限 1 1 3 3 2 2 6
学分 6 7 2 1 4 5 1
最多可以获得15学分,其中一个完成作业的次序为2,6,3,1,7,5,4,注意可能d还有其他方法。

你的任务就是找到一个完成作业的顺序获得最大学分。

输入

第一行一个整数N,表示作业的数量。

接下来N行,每行包括两个整数,第一个整数表示作业的完成期限,第二个数表示该作业的学分。

N<=1000000,作业的完成期限小于1000。

输出

输出一个整数表示可以获得的最大学分。保证答案不超过longint范围。

题解:题目意思是每个作业都有不同的价值和要在某天前完成,每天只能完成一个作业求,求价值的最大值,贪心和并查集的一点运用。
贪心策略:
1.学分多的一定要拿。
2.每次要拿学分的作业一定要在离它期限最近且没被使用过的天数。
并查集:
可以用递归写,这边没用递归可以在2MS内跑完,用递归得5秒,看题目给的时长,用一个pre数组来标记某天有没有被使用过,如果没指向自己,当被使用过后会指向前一天,当前一天也被使用后要路径压缩。
优先队列:
找到所有作业中最大的一天,排序完后从最大的一天开始遍历,如果当前作业能够在今天完成,加入队列,加完后取最大价值的出来就好了,其实差不多。

并查集

#include
using namespace std;
const int maxn=1000005;
const int maxn1=700005;
int pre[maxn1];
struct node{
    int t,val; //结构体,时间和价值
}q[maxn];
bool cmp(node a,node b){
    return a.val>b.val; // 价值大的排在前面
}
int getfather(int x){
    int son=x,tmp;	
    while(pre[x]!=x){	//当前的父亲不指向自己时,继续找
        x=pre[x];
        if(x==0)	//当找到0说明当前任务的最大期限内所以的时间都被使用完了
            return 0;
    }
    while(son!=x){
        tmp=pre[son];	//路径压缩,把父亲节点的所有儿子都设为x-1
        pre[son]=x-1;
        son=tmp;
    }
    pre[x]=x-1;	//父亲节点用完后也得指向x-1
    return 1;
}
int main(){
    int n;
    __int64 ans=0;
    scanf("%d",&n);
    for(int i=0;i<maxn1;i++)
        pre[i]=i;	//初始化所有天数指向自己
    for(int i=0;i<n;i++)
        scanf("%d%d",&q[i].t,&q[i].val);
    sort(q,q+n,cmp);	
    for(int i=0;i<n;i++) if(getfather(q[i].t)) ans+=q[i].val;//如果未被使用
    printf("%I64d",ans);
}

优先队列

#include
using namespace std;
const int maxn=1000005;
struct node{
    int t,val;
}e[maxn];
bool cmp(node a,node b){
    return a.t>b.t; 
}
priority_queue<int>q;
int main(){
    int n,m=0;
    __int64 ans=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d",&e[i].t,&e[i].val); // 输入排序
        m=max(m,e[i].t);
    }
    sort(e,e+n,cmp);
    int l=0;
    for(int i=m;i>0;i--){
        while(e[l].t>=i&&l<n) q.push(e[l++].val); //遍历今天能做的所有作业
        if(!q.empty()){ //取出价值最大的
            ans+=q.top();
            q.pop();
        }
    }
    printf("%I64d\n",ans);
}

你可能感兴趣的:(贪心)