zoukankan      html  css  js  c++  java
  • 【题解】一本通1430:家庭作业

    题目

    1430:家庭作业

    时间限制: 1000 ms 内存限制: 65536 KB
    提交数: 1988 通过数: 467

    【题目描述】

    老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为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行,每行包括两个整数,第一个整数表示作业的完成期限,第二个数表示该作业的学分。

    【输出】

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

    【输入样例】

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

    【输出样例】

    15

    分析

    乍一看,这题和一本通的智力大冲浪简直是一模一样!
    智力大冲浪代码

    #include <cmath>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #define N 510
    #define F(i,a,b) for(int i=a;i<=b;i++)
    #define UF(i,a,b) for(int i=a;i>=b;i--)
    
    using namespace std;
    
    bool visit[N];
    int n,m,ot;
    
    struct game{
    	int e, ot;
    }G[N];
    
    bool cmp(game a, game b){
    	return a.ot > b.ot;
    }
    
    int main()
    {
    	scanf("%d%d",&m,&n);
    	F(i,1,n){
    		scanf("%d",&G[i].e);
    	}
    	F(i,1,n){
    		scanf("%d",&G[i].ot);
    	}
    	sort(G+1,G+1+n,cmp);
    	/*F(i,1,n){
    		cout << G[i].e << " " << G[i].ot << endl; 
    	}*/
    	F(i,1,n){
    		int t = 0;
    		UF(j,G[i].e,1){
    			if(!visit[j]){
    
    				visit[j]++;
    				t = 1;
    				break;
    			}
    		}
    		if(!t){
    			ot += G[i].ot;
    		}
    
    	}
    	m-=ot;
    	cout << m << endl;
    	return 0;
    }
    

    但是,按照原写法,这题是会超时的,就算加快读,也要TLE两个点
    所以必须要加两个优化

    1

    发现,如果当前物品已经无处可放,也就是说可放的日期已经放满,那我们的第二层循环就是完全无用且费时的
    所以,我们可以一个变量q来记录前几个日期已被放满,如果G[i]<=q,那么当前物品就无处可放了
    发现有物品无处可放时,也就是进行了一次无用循环后,q更新

    2

    然而只有上面的优化是无法解决问题的
    从别人那知道,这是可以用并查集优化的
    用并查集代替第二层循环
    先copy一下别人的code

    
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline int mymax(int x,int y){return x>y?x:y;}//这个是max的更快版 
    inline int read()//日常快读,时间减少400多ms 
    {
    	char c=getchar();
    	int x=0,f=1;
    	while(c<48 || c>57)
    	{
    		if(c=='-') f=-1;
    		c=getchar();
    	}
    	while(c>=48 && c<=57)
    	{
    		x=x*10+c-48;
    		c=getchar();
    	}
    	return x*f;
    }
    struct node//结构体排序 
    {
    	int x,y;
    }a[1110000];
    int cmp(node n1,node n2)
    {
    	return n1.y>n2.y;
    }
    int fa[710000];
    int find(int x)//这个如果我没记错的话是并查集里面用到的找父亲
    /*
    	这个找父亲在这里的作用就是判断当前的学分能不能获取
    	其实和我之前打的那个代码是一样的
    	就是一个一个循环,递减
    	然后再判断是不是每一步都失败 
    */ 
    {
    	if(fa[x]==x) return x;
    	/*
    		如果当前的fa数组的期限与当前的期限一样
    		就把x返回给fx,说明当前这个a[i].x的这个期限是可以用的
    		就可以增加学分 
    	*/ 
    	else return fa[x]=find(fa[x]);
    	/*
    		否则的话,就返回fa[x],其实是减了1之后的值
    		说实在的这个函数解析起来不容易
    		大家还是调试吧,之前的那个代码理解起来会容易一些 
    	*/ 
    }
    int main()
    {
    	int n; n=read(); 
    	int ans=0;
    	int maxx=0;
    	for(int i=1;i<=n;i++)
    	{
    		a[i].x=read();
    		a[i].y=read();
    		maxx=max(maxx,a[i].x);//maxx记录最大期限 
    	}
    	for(int i=1;i<=maxx;i++) fa[i]=i;//最开始的fa数组与完成期限同步 
    	sort(a+1,a+n+1,cmp);//排序 
    	for(int i=1;i<=n;i++)
    	{
    		int fx=find(a[i].x);//fx记录返回回来的值 
    		if(fx!=0)
    		//如果不等于0,说明当前这个期限还有可以完成的余地 
    		{
    			ans+=a[i].y;//就增加学分 
    			fa[fx]=fx-1;//然后这一步其实就是之前的j-- 
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    ————————————————
    版权声明:本文为CSDN博主「TJ.」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_42367531/article/details/84329966
    }
    

    发现用fa[]数组来存储对于第G[i].e天,离G[i].e最近的并且没有被使用过的是哪一天,在不断的更新(fa[x]=find(fa[x])中,find()可以达到常数查询
    解释一下
    fa[fx]=fx-1;//然后这一步其实就是之前的j--
    就是fx这一天用掉之后,fa[fx]肯定要小于fx了,就减了个一,其实fa[fx]=fa[fx-1]也可以达到一样的效果,但是提交后是比前者要慢一丢丢(不知道为啥)

    并查集优化,确实很巧妙
  • 相关阅读:
    MSBuild最佳实践
    Javascript:阻止浏览器默认右键事件,并显示定制内容
    zeptoJS:如何像jQuery一样,让滚动变得优雅?
    Javascript:DOM表格操作
    Javascript:getElementsByClassName
    Javascript:DOM动态创建元素实例应用
    Javascript:倒计时
    Javascript:sort()方法快速实现对数组排序
    探究css !important的应用之道
    Javascript:splice()方法实现对数组元素的插入、删除、替换及去重
  • 原文地址:https://www.cnblogs.com/ZhengkunJia/p/12264637.html
Copyright © 2011-2022 走看看