贪心算法经典例题

贪心算法:每次都取最佳

牛吃草

链接:https://ac.nowcoder.com/acm/problem/24867
来源:牛客网
题目
Each of Farmer John’s N (1 <= N <= 50,000) cows likes to graze in a certain part of the pasture, which can be thought of as a large one-dimeensional number line. Cow i’s favorite grazing range starts at location Si and ends at location Ei (1 <= Si < Ei; Si < Ei <= 100,000,000).
Most folks know the cows are quite selfish; no cow wants to share any of its grazing area with another. Thus, two cows i and j can only graze at the same time if either Si >= Ej or Ei <= Sj. FJ would like to know the maximum number of cows that can graze at the same time for a given set of cows and their preferences.
Consider a set of 5 cows with ranges shown below:
… 1 2 3 4 5 6 7 8 9 10 11 12 13 …
… |----|----|----|----|----|----|----|----|----|----|----|----|----
Cow 1: <=:=> : : :
Cow 2: <:::=>:
Cow 3: : <
> : : :
Cow 4: : : <====:=> :
Cow 5: : : <
> : :

These ranges represent (2, 4), (1, 12), (4, 5), (7, 10), and (7, 8), respectively.
For a solution, the first, third, and fourth (or fifth) cows can all graze at the same time. If the second cow grazed, no other cows could graze. Also, the fourth and fifth cows cannot graze together, so it is impossible for four or more cows to graze.
输入
5
2 4
1 12
4 5
7 10
7 8
输出
3
大概意思:每只牛都要吃草,题目给出牛吃草的开始和结束时间,求给定一组奶牛在同一时间可以吃草的最大奶牛数量以及它们的偏好。
解决方案:这就可以联想到区间覆盖的问题(工作安排),在相同开始的时间上找到最早结束时间。

  ... 1    2    3    4    5    6    7    8    9   10   11   12   13 ...
  ... |----|----|----|----|----|----|----|----|----|----|----|----|----
Cow 1:      <===:===>          :              :              :
Cow 2: <========:==============:==============:=============>:
Cow 3:          :     <====>   :              :              :
Cow 4:          :              :     <========:===>          :
Cow 5:          :              :     <==>     :              :
  1. 先将工作时间根据结束时间进行排序,这样就可以将结束时间早点先拿出来。这时候就是cow1、cow3、cow5、cow4、cow2.
  2. 然后再将最先结束的时间和第二个开始的时间进行比较,必须要在一只奶牛吃完后第二支奶牛才能继续吃,所以后面一只的开始时间需要大于前面一只的结束时间。
  3. 这时候需要使用循环,并且在每次比较成功之后进行赋值,因为是根据上一个结束最早时间进行比较。
import java.util.*;
 
 
public class Main {
 
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N=scanner.nextInt(),A=1;
        //开一个二维数组,分别代表牛吃草的开始与结束时间。
        int[][] nums=new int[N][2];
        for(int i=0;i<nums.length;i++){
            for(int j=0;j<nums[i].length;j++){
                nums[i][j]=scanner.nextInt();
            }
        }
       // 重写sort的排序方法,能根据数组的第二列进行排序
        Arrays.sort(nums,new Comparator<int[]>(){
            public int compare(int[] a,int[] b){
                if(a[1]==b[1]){
                    //若a[0] b[0]值相同则比较a[1] b[1],按升序
                    //返回小的那一个
                    return a[0]-b[0];
                }else{
                    return a[1]-b[1];
                }
            }
        });
        int endTime=nums[0][1];
       //将结束早的时间与第二个开始时间进行比较。
        for(int i=1;i<N;i++){
            if(nums[i][0]>=endTime){
                A++;
                endTime=nums[i][1];
            }
        }
        System.out.println(A);
    }
}

拓展:集合排序

class ComparatorDp implements Comparator{
         public int compare(Object obj1, Object obj2) {
            double temp=obj1.value()-obj2.value();
            int a = 0;
            if (temp>0) {
                a = -1;                           //代表obj1在前
            } else {
                a = 1;                           //代表obj1在后
            }
            return a;
        }
    }

国王的游戏

题目
恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
输入描述:
第一行包含一个整数 n ,表示大臣的人数。
第二行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
输出描述:
一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
输入
3
1 1
2 3
7 4
4 6
输出
2
思路

若有AB两大臣

  1. A在B前(AB),则A1: Π / R ( A ) \Pi/R(A) Π/R(A)                      B1: Π ∗ L ( A ) / R ( B ) \Pi *L(A)/R(B) ΠL(A)/R(B)
  2. B在A前(BA),则A2: Π ∗ L ( B ) / R ( A ) \Pi*L(B)/R(A) ΠL(B)/R(A)        B2: Π / R ( B ) \Pi /R(B) Π/R(B)

假设A在前面更好,则 m a x ( A 1 , B 1 ) < = m a x ( A 2 , B 2 ) max(A1,B1)<=max(A2,B2) max(A1,B1)<=max(A2,B2)
因为 A 1 < = A 2 , B 1 > = B 2 A1<=A2,B1>=B2 A1<=A2,B1>=B2,所以需要 B 1 < = B 2 B1<=B2 B1<=B2
L ( B ) / R ( A ) < = L ( A ) / R ( B ) = = = = > L ( A ) R ( A ) < = L ( B ) R ( B ) L(B)/R(A)<=L(A)/R(B)====>L(A)R(A)<=L(B)R(B) L(B)/R(A)<=L(A)/R(B)====>L(A)R(A)<=L(B)R(B)

也就是说先计算每位大臣左手和右手的乘积,然后按升序排序,这样就是将获得奖赏最多的大臣,所获得奖赏尽可能的少。
!!!注意:1. 需要高精度,所以要用大数类型
2. 排序后在最后一位的大臣并不一定是获得金币最多的人

比如 (1,1) (6,2) (13,1) (1,15)—>这是排好序之后的,如果按照最后一个大臣获取金币最多,那么结果是5
但是第二个大臣获取的金币是1*6/6=6 更多,所以在后面需要不断维护最大值,刷新。


import java.math.BigInteger;
        import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n=scanner.nextInt();
        BigInteger a=scanner.nextBigInteger(),b=scanner.nextBigInteger();
        LandR[] landRS=new LandR[n];
        for (int i=0;i<landRS.length;i++){
        //对象数组进行赋值的时候需要new一个新的空间,否则会报空值。
            landRS[i]=new LandR(scanner.nextBigInteger(),scanner.nextBigInteger());
        }
        Arrays.sort(landRS, new Comparator<LandR>() {
            @Override
            public int compare(LandR o1, LandR o2) {
                return (o1.getRight().multiply(o1.getLeft())).subtract(o2.getRight().multiply(o2.getLeft())).intValue();
            }
        });
        BigInteger ans=new BigInteger("0");
        for (int i=0;i<landRS.length;i++){
        	//不断对最大值进行刷新
            ans=ans.max(a.divide(landRS[i].getRight()));
            //累乘
            a=a.multiply(landRS[i].getLeft());
        }
        System.out.println(ans);
    }
}
class LandR{
    public BigInteger left;
    BigInteger right;

    public BigInteger getLeft() {
        return left;
    }

    public void setLeft(BigInteger left) {
        this.left = left;
    }

    public BigInteger getRight() {
        return right;
    }

    public void setRight(BigInteger right) {
        this.right = right;
    }

    public LandR(BigInteger left, BigInteger right) {
        this.left = left;
        this.right = right;
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值