zoukankan      html  css  js  c++  java
  • 综合实践作业1/2(贪心)

    兼容任务

    1.题目描述

    设有n个任务,其中每个任务有一个起始时间si和一个结束时间ei,且si<ei,同一时间只能完成一个任务。如果选择了任务i ,则它在时间区间 [si ,ei) 内占用资源。若区间 [si ,ei) 与区间 [sj, ej)不相交,则称任务 i 与任务 j 是相容的。那么,对于给定的任务时间区间,能互相兼容的最大任务个数是多少呢?

    输入格式:

    第一行一个整数n (1<=n<=1000) ;
    接下来n行,每行两个整数si 和 ei。

    输出格式:

    互相兼容的最大任务个数。

    输入样例:

    4
    1 3
    4 6
    2 5
    1 7

    输出样例:

    2

    2.实现方法

      这题的难点在于如何找到能互相兼容的最大任务个数,就是要思考能够找到最优解的方法。仔细看题我们可以发现,在同样满足条件的下一个时间段任务中,选择越早结束的那个,越能为后面的任务腾出时间。所以我们可以以结束时间为标准将任务降序排列,不断选择结束时间较早的任务,就能获得最优解。

    #include<stdio.h>
    int main(){
    	int n,r;
    	scanf("%d",&n);
    	struct item{
    		int a,b;
    		int c;
    	}time[n];
    	for(int i=0;i<n;i++){
    		scanf("%d %d",&time[i].a,&time[i].b);
    		time[i].c=i;
    	}
    	struct item temp;
    	for(int i=0;i<n-1;i++){
    		for(int j=0;j<n-1-i;j++){
    			if(time[j].b>time[j+1].b){
    				temp=time[j];
    				time[j]=time[j+1];
    				time[j+1]=temp;
    			}  
    		}
    	}
    	int count=1;r=0;
    	for(int j=1;j<n;j++){
    		if(time[r].b<=time[j].a){
    			count++;
    			r=j;	
    		}
    	}
    	printf("%d",count);
    }
    
    

    3.思路总结

      这类题目主要重在思考和经验总结,可以尝试用逆向、化繁为简的方式来思考。

    均等笔(均分糖果)

    1.题目描述

    n个人围成一圈,每人有ai支笔。每人可以向左右相邻的人传递笔,每人每次传递一支笔消耗的能量为1。求使所有人获得均等数量的笔的最小能量。

    输入格式:

    第一行一个整数n ,表示人的个数(30%的数据,n<=1000;100%的数据,n<=1e6)。
    接下来n行,每行一个整数 ai。

    输出格式:

    输出一个整数,表示使所有人获得均等笔的最小能量。(答案保证可以用64位有符号整数存储)

    输入样例:

    4
    1
    2
    5
    4

    输出样例:

    4

    2.实现方法

    为解决该道题首先要理解:
    (1)均分笔该题的圈可以从任意位置断开变成一列转化为均分纸牌问题。
    (2)均分纸牌问题最优解的实现是从左到右(除最后一个人外)每人传递一次(若已经达到平均值的则跳过),从左到右某人传递的手牌书等于从左到该人的所有原手牌之和减去平均数的人数倍,把所有人传递的手牌数加起来即为传递的最小纸牌数。
    (3)而均分笔这题由于是一个圈,所以可以把它看作循环均分纸牌问题,那么到这一步主要的问题就是从哪个位置解开能够保证传递的纸牌数最小,这里可以根据数学关系严格证明或者大致的判断得出,传递纸牌数中位数所代表的人的位置将圈解开可以获得最小的传递数。(严格证明过程可参见链接)
    参考P1031均分纸牌 P2512糖果传递daolao题解

    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    int main(){
    	long long n;
    	scanf("%lld",&n);
    	long long a[n],ave=0;
    	for(int i=0;i<n;i++){
    		scanf("%lld",&a[i]);
    		ave+=a[i];
    	}
    	ave/=n; 
    	for(int i=0;i<n;i++){
    		a[i]=a[i]-ave;
    	}
    	for(int i=0;i<n-1;i++){
    		a[i+1]=a[i]+a[i+1];
    	}
    	sort(a,a+n);
    	int mid=n/2;long long count=0;
    	for(int i=0;i<n;i++){
    		count+=abs(a[i]-a[mid]);
    	}
    	printf("%lld",count);
    	return 0;
    }
    

    PS.这里提前减去ave可以使计算前缀和的过程变得更简洁

    3.思路总结

      这题一开始是想直接通过算出每种解开方式的再比较大小得出最小传递纸牌数,但是明显复杂度过大,不可行。最后尝试学习理解了这种巧妙的方法才得以解决。
    这题主要的难点在于你要发现变化中潜在的不变量,找到转化的办法,然后跟据直觉和数学知识进行判断,最后找到实现方法。

  • 相关阅读:
    review01
    在win+r中常用的命令
    shutdown命令
    XML解析,出现ClassCastException 原因
    XML解析,出现ClassCastException 原因
    韩顺平循序渐进学JAVA从入门到精通 视频全套,需要的联系我
    韩顺平循序渐进学JAVA从入门到精通 视频全套,需要的联系我
    线程池,以后有时间看
    线程池,以后有时间看
    利用线程分离发送和接受,这样每个客服端都可以分离
  • 原文地址:https://www.cnblogs.com/beyondzones/p/12398045.html
Copyright © 2011-2022 走看看