Java算法学习5——贪心法解决简单贪心与区间贪心问题

这篇博客介绍了贪心算法在解决区间不相交问题和区间选点问题中的应用。首先,对于区间不相交问题,通过定义结构体存储区间,按左端点降序、右端点升序排序,然后遍历找到不相交的最大区间数量。其次,对于区间选点问题,同样排序后选择最左边的点,确保每个区间至少有一个点。博客还提供了Java实现的代码示例,并强调了排序和实例化数组时的注意事项。
摘要由CSDN通过智能技术生成

一、简单贪心

当题目中让我们最优的情况时,比如效益最大,组成的数最大等等,此时我们要想到贪心算法的思想。简单来说就是先去考虑局部最优,然后由局部最优组成全局的最优。比如,当求效益最大时,要达到效益最大,说明每个单独的个体的效益最大,所以我们可以先去求单价效益最大的情况开始;组成的数最大,那么就说明,每一个位放的数要最小,这也是局部最优,最后组成的数就是最小,达到了全局最优。

二、区间贪心(区间不相交问题与区间选点问题)

1.区间不相交问题

(1)问题描述:给出N个开区间,从中选择尽可能多的开区间,使得这些开区间两两没有交集

(2)解决问题的步骤

①先定义一个结构体数组将区间全部存入
②对区间进行排序:按照左端点从大到小排序,如果左端点相同,则按照右端点从小到大排序,原因请看《算法笔记》P122
②然后遍历排好序的区间数组,选择第一个区间为标准,从第二个区间开始,如果该区间的右端点在选定的标准区间的左端点的左边,则更新标准区间为它,然后不相交的区间的数量+1

(3)代码如下

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

class Inteval {
    public int left,right;
    Inteval(int a,int b) {
        this.left = a;
        this.right = b;
    }
}

 class cmp implements Comparator<Inteval>{
     @Override
    public int compare(Inteval o1,Inteval o2) {
        if(o1.left!=o2.left){
            return o1.left>o2.left?-1:1;//按照left降序排序
        }
        else{
            return o1.right>o2.right?1:-1;//按照right升序排序
        }
    }

 }
public class Test {
    public static void main(String []args){
        int n;
        Scanner cin=new Scanner(System.in);
        n=cin.nextInt();
        Inteval []I=new Inteval[n+1];
        for(int i=0;i<n;i++){
            int left=cin.nextInt();
            int right=cin.nextInt();
            I[i]=new Inteval(left,right);//太容易错了,要先给数组中的每一个元素开辟一个空间,否则都是空的
        }
        Arrays.sort(I,0,n, new cmp());
        for(int i=0;i<n;i++){
            System.out.println(I[i].left+" "+I[i].right);
        }

        //ans用来记录不相关的区间的个数,lastX用来记录上一个被选中区间的左端点
        int ans=1,lastX=I[0].left;
        for(int i=1;i<n;i++){
            if(I[i].right<=lastX){
                ans++;
                lastX=I[i].left;
            }
        }
        System.out.println(ans);
    }
}

(4) 知识点及易错点

①Java如何用Scanner进行连续输入
②Java如何自定义sort对类进行排序
③当建立一个类的数组时,对数组的元素进行复制的时候,要先给每一个元素先new,然后才可以赋值
以上的知识点都总结到了这个帖子上,详情请看:
https://blog.csdn.net/Warddamn/article/details/114269664

2.区间选点问题

(1)问题描述:给出N个闭区间,求最少需要确定多少个点,才能使每个闭区间中都至少存在一个点。

(2)解决问题的步骤

①先定义一个结构体数组将区间全部存入
②对区间进行排序:按照左端点从大到小排序,如果左端点相同,则按照右端点从小到大排序,原因请看《算法笔记》胡凡版P122
②然后遍历排好序的区间数组,选择第一个区间为标准,从第二个区间开始,如果该区间的右端点在选定的标准区间的左端点的左边**(注意此时是不能带等号的)**,则更新标准区间为它,然后不相交的区间的数量+1

(3)代码实现

与1的代码完全一致,除了将小于等于改成小于,即如下:

if(I[i].right<lastX) {...}

原因请看《算法笔记》胡凡版P122

--------------------------------------------3.24 更新笔记--------------------------------------

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

/*
 * 本节重新说明了比较器的问题,比较器
 * compare(A a, A b)
 * 当a-b的时候为升序排序,b-a的时候为降序排序
 * 若a和b为字符串,则a.compareTo(b)的时候为升序排序,b.compareTo(a)为降序排序
 * 因此,如果有特殊的对a=b有要求的话可以这么写
 * 	Comparator<Greed> cmp=new Comparator<Greed>() {
		public int compare(Greed a,Greed b) {
			if(a.x!=b.x) {
				return b.x-a.x;
			}else {
				return a.y-b.x;
			}
		}
	};
 *  如果没有的话,就可以直接这样
 *  Comparator<Greed> cmp=new Comparator<Greed>() {
		public int compare(Greed a,Greed b) {
		    return a.x-b.x;//升序
		}
	};
* 详情可以看如下网站:
* https://lhf2018.github.io/2019/10/20/Java%E9%87%8D%E5%86%99%E6%AF%94%E8%BE%83%E5%99%A8/
*
*易错知识点:
*(1) 在主类中新建子类或者全局变量要加static。
*(2) 当建立一个新类子类时,要写构造函数。
*(3) 当建立一个新类数组的时候,要对数组中的每一个元素进行实例化。
*
 */

public class _区间贪心 {
	static class Greed//新建类要设置构造函数,此外,在主类里面建立全局变量或者是子类的话一定要加static
	{
		int x=0;
		int y=0;
		Greed(){};
		
	}
	static Comparator<Greed> cmp=new Comparator<Greed>() {
		public int compare(Greed a,Greed b) {
			if(a.x!=b.x) {
				return b.x-a.x;//降序排序
			}else {
				return a.y-b.y;
			}
		}
	};
	
	public static void main(String[] args) {
		Scanner cin=new Scanner (System.in);
		int n;n=cin.nextInt();
		Greed []greed=new Greed[n];//新建了新类型的数组,一定要给每个元素初始化
		for(int i=0;i<n;i++) {
			greed[i]=new Greed();
			greed[i].x=cin.nextInt();
			greed[i].y=cin.nextInt();
		}
		//测试比较器
		Arrays.sort(greed,cmp);
		for(int i=0;i<n;i++) {
			System.out.println(greed[i].x+","+greed[i].y);
		}
		//解决问题,下一个的右端点要小于等于当前这个的左端点
		int count=1; Greed last=greed[0];
		for(int i=1;i<n;i++) {
			if(last.x>=greed[i].y) {
				last=greed[i];
				count++;
			}
		}
		System.out.println(count);
	}
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值