JAVA-贪心算法

本文通过四个实例详细介绍了贪心算法在项目安排、金条切割、最大利润获取和N皇后问题中的应用。在项目安排中,按照会议结束时间早的优先级进行选择;在金条切割问题中,优先切割最长的金条;在做项目问题中,优先选取利润最高的项目;在N皇后问题中,使用递归策略避免皇后在同一行、列或对角线上。贪心算法和排序策略在这些问题中展示了高效解决问题的能力。
摘要由CSDN通过智能技术生成

(代码如有错误,烦请指正)
***贪心算法:***在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫做贪心算法。即不从整体最优上加以考虑,做出的是在某种意义上的局部最优解。
贪心算法的解题思路:首先实现一个不依靠贪心策略的解法;接着利用对数器依次对比贪心策略1、2、3等,利用实验来判断哪个贪心策略是正确的。
注:不要纠结贪心算法的证明问题

1.一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲,给你每一个项目的开始时间和结束时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行宣讲的场次最多。返回这个最多的宣讲场次。 贪心策略:哪个会议结束时间早,选哪一个。

//会议的格式
class Program{
    int start;
    int end;
    public Program(int start,int end){
        this.start=start;
        this.end=end;
    }
}
    //使用暴力递归解法
    public static int bestArrange(Program[] programs){
        if(programs==null||programs.length==0){
            return 0;//没有预定的会议
        }
        return process(programs,0,0);
    }
    //programs代表还没被安排的会议,done代表已经被安排的会议数,timeLine表示当前的时间
    public static int process(Program[] programs,int done,int timeLine){
        if(programs.length==0){
            return done;
        }
        int max=done;
        for(int i=0;i<programs.length;i++){
            if(programs[i].start>=timeLine){
                Program[] exceptPrograms=copyButExceptI(programs,i);
                max=Math.max(max,process(exceptPrograms,done+1,programs[i].end));
            }
        }
        return max;
    }
    public static Program[] copyButExceptI(Program[] programs,int i){
        Program[] exceptPrograms=new Program[programs.length-1];
        int index=0;
        for(int k=0;k<programs.length;k++){
            if(k!=i){
                exceptPrograms[index]=programs[k];
                index++;
            }
        }
        return exceptPrograms;
    }
    //使用贪心算法
    //定义比较器
    public static class programCompartor implements Comparator<Program> {
        @Override
        public int compare(Program p1,Program p2){
            return p1.end-p2.end;
        }
    }
    public static int bestArrange2(Program[] programs){
        PriorityQueue<Program> prior=new PriorityQueue<>(new programCompartor());
        int timeLine=0;
        for(int i=0;i<programs.length;i++){
            prior.add(programs[i]);
        }
        int num=0;
        while(!prior.isEmpty()){
            Program cur=prior.poll();
            if(cur.start>=timeLine){
                num++;
                timeLine=cur.end;
            }
        }
        return num;
    }

2.金条切割:一条金条切分成两半,需要花费和长度相同的钱。输入一个数组,返回分割花费的最小钱数。(金条的长度等于数组元素之和)

贪心策略:先把最长的切分好。

    //暴力递归解决
    public static int goldSplit(int[] arr){
        if(arr==null||arr.length==0){
            return 0;
        }
        return process(arr,0);
    }
    //arr代表还需分割的金条的长度数组,cost代表已经花费的数量
    public static int process(int[] arr,int cost){
        if(arr.length==1){
            return cost;
        }
        int min=Integer.MAX_VALUE;
        //暴力遍历,看看数组中的哪两个元素合并花费最小
        //i<arr.length-1和i<arr.length应该是都可以,因为arr.length=1时,会直接返回
        for(int i=0;i<arr.length-1;i++){
            for(int j=i+1;j<arr.length;j++){
                min=Math.min(min,process(sumArr(arr,i,j),cost+arr[i]+arr[j]));
            }
        }
        return min;//sunArr函数相当于已经记录了之前合并金条的花费,所以直接返回min即可
    }
    public static int[] sumArr(int[] arr,int i,int j){
        int[] newArr=new int[arr.length-1];
        int index=0;
        for(int k=0;k<arr.length;k++){
            if(k!=i && k!=j){
                newArr[index]=arr[k];
                index++;
            }
        }
        newArr[index]=arr[i]+arr[j];//该次合并金条的花费
        return newArr;
    }
    //使用贪心算法(数组排序太麻烦了,不如使用小根堆)
    public static int goldSplit2(int[] arr){
        PriorityQueue<Integer> prior=new PriorityQueue<Integer>();
        for(int i=0;i<arr.length;i++){
            prior.add(arr[i]);
        }
        int cost=0;
        while(prior.size()>1){
            int a=prior.poll();
            int b=prior.poll();
            int sum=a+b;
            cost+=sum;
            prior.add(sum);
        }
        return cost;
    }

3.做项目问题:有初始资金和能做的项目个数限制,给定一批项目的花费和利润,求最后能获得的最大钱数。
贪心策略:在能做的项目中,选利润最大的一个项目做。

    public static class Program{
        int cost;//做该项目需要花费的钱
        int get;//做该项目可以得到的利润
        public Program(int cost,int get){
            this.cost=cost;
            this.get=get;
        }
    }
    //programs是所有的项目信息,startMoney是初始启动资金,num是最多能做的项目个数
    public static int maxMoney(Program[] programs,int startMoney,int num){
        PriorityQueue<Program> costPrior=new PriorityQueue<>(new costCompartor());
        PriorityQueue<Program> getPrior=new PriorityQueue<>(new getCompartor());
        //把所有的项目按照cost放入小根堆中
        for(int i=0;i<programs.length;i++){
            costPrior.add(programs[i]);
        }
        int done=0;//用来记录一共做了多少个项目
        while(done<num){
            while(!costPrior.isEmpty() && costPrior.peek().cost<=startMoney){
                getPrior.add(costPrior.poll());//将所有可以做的项目放入到大根堆中
            }
            if(getPrior.isEmpty()){
                return startMoney;//大根堆为空,代表当前没有可做的任务,直接返回当前手头的资金即可
            }
            startMoney+=getPrior.poll().get;//做大根堆中利润最大的项目,并获得相应的利润
            done+=1;//记录目前一共做了多少个项目
        }
        return startMoney;//做完num个项目后,返回所得的资金
    }
    //重写比较方法,按照cost排成小根堆
    public static class costCompartor implements Comparator<Program>{
        @Override
        public int compare(Program p1,Program p2){
            return p1.cost- p2.cost;
        }
    }
    //重写比较方法,按照获得的利润排成大根堆
    public static class getCompartor implements Comparator<Program>{
        @Override
        public int compare(Program p1,Program p2){
            return p2.get-p1.get;
        }
    }

4.N皇后问题:在N*N的棋盘上拜N个皇后,任意两个皇后不同行、不同列、不在同一条斜线上,返回有多少种摆法。

暴力递归

//n皇后问题
public class Five {
    public static int nQueenNum(int n){
        if(n<1){
            return 0;
        }
        int[] record=new int[n];
        return process(0,record,n);
    }
    //i表示当前的行数,record用来记录皇后放置的位置,n表示一共有多少行
    public static int process(int i, int[] record,int n){
        if(i==n){
            return 1;//到了最后一行,只能有一种可能性
        }
        int sum=0;
        //j表示皇后位于第i行第j列
        for(int j=0;j<n;j++){
            if(isValid(record,i,j)){
                record[i]=j;
                sum+=process(i+1,record,n);
            }
        }
        return sum;
    }
    public static Boolean isValid(int[] record,int i,int j){
        for(int k=0;k<i;k++){
            if(j==record[k] || Math.abs(record[k]-j)==Math.abs(k-i)){
                return false;
            }
        }
        return true;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值