贪心

本文介绍了多种区间问题的解决方案,包括最大不相交区间数量、最少箭引爆气球、无重叠区间、区间覆盖、划分字母区间、区间合并等。这些问题通过排序、比较和更新策略来找到最优解。同时,还涉及了其他类型的问题,如重置更新问题(最大连续子数组的和)、加油站问题、股票买卖策略、跳跃游戏等,展示了不同场景下的算法应用。
摘要由CSDN通过智能技术生成

区间问题

最大不相交区间数量

在这里插入图片描述

将所有区间按照右端点排序,如果目前区间左端与上一个区间不相交,则点数增加,更新右端点

import java.util.*;
import java.io.*;
class Seg{//保存区间
	int l,r;
	public Seg(int l,int r){//构造器
		this.l=l;
		this.r=r;
	}
}
public class Main{
	public static void main(String[] args)throws IOException {
		Scanner sc=new Scanner(System.in);
		int n=Integer.parseInt(sc.nextLine());//区间数
		Seg[]segs=new Seg[n];//区间数组
		//读取区间
		for(int i=0;i<n;i++){
			String []str=sc.nextLine().split(" ");
			int l=Integer.parseInt(str[0]);
			int r=Integer.parseInt(str[1]);
			segs[i]=new Seg(l,r);
		}
		Arrays.sort(segs,new Comparator<Seg>(){
			public int compare(Seg seg1,Seg seg2){
				return seg1.r-seg2.r;//按照区间右端点排序
			}
		});
		int res=0;
		int end=Integer.MIN_VALUE;//初始化上一个区间的右端点
		for(Seg s:segs){
			if(s.l>end){//当前区间左端与前一个区间右端不相交
				res++;
				end=s.r;//更新下一个区间右端
			}
		}
		System.out.println(res);
	}
  }

最少箭引爆气球

在这里插入图片描述
按照左端点排序,到达重叠起球最小边界就res++,否则更新当前重叠最小边界

class Solution {
    public int findMinArrowShots(int[][] points) {
        //按照左端点排序
        Arrays.sort(points,(a,b)->Integer.compare(a[0],b[0]));
        int res=1;
        for(int i=1;i<points.length;i++){
            if(points[i][0]>points[i-1][1]){//不相交了
                res++;
            }else{
                //更新右边界最小值
                points[i][1]=Math.min(points[i][1],points[i-1][1]);
            }
        }
        return res;
    }
}

无重叠区间

在这里插入图片描述
按照左端点排序,前后不相交时res++,最后移除的区间用总的区间减去不重叠区间的最大值(res)

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        int res=1;
        for(int i=1;i<intervals.length;i++){
            if(intervals[i][0]>=intervals[i-1][1]){
                res++;
            }else{
                intervals[i][1]=Math.min(intervals[i][1],intervals[i-1][1]);
            }
        }
        return intervals.length-res;
    }
}

区间覆盖

在这里插入图片描述

import java.util.*;
class Range implements Comparable<Range>{
    int l,r;
    public int compareTo(Range o){
        return Integer.compare(l,o.l);//按照左端点排序
    }
    public Range(int l,int r){
        this.l=l;
        this.r=r;
    }
}
public class Main{
        static int N=100010;
        static Range[]range=new Range[N];
    public static void main(String[]args){
        Scanner sc=new Scanner(System.in);
        int st=sc.nextInt();
        int ed=sc.nextInt();
        int n=sc.nextInt();
        for(int i=0;i<n;i++){
            int l=sc.nextInt();
            int r=sc.nextInt();
            range[i]=new Range(l,r);
        }
        Arrays.sort(range,0,n);//按照左端点排序
        
        int res=0;//区间数
        boolean pass=false;
        for(int i=0;i<n;i++){
            int j=i;
            int end=(int)-(2e9);
            while(j<n&&range[j].l<=st){//左端点包含了当前区间起点
                end=Math.max(end,range[j].r);//更新最右端能伸到的位置
                j++;
            }
            if(end<st){//不能包含,跳到下一次区间
              break;
            } 
            
            res++;//满足条件的区间数++
            
            if(end>=ed){//结束
                pass=true;
                break;
            }
            st=end;//更新起点为最右的端点
           
            i=j-1;//下一次从j走过的之后开始走
        }
        if(!pass)res=-1;
        System.out.println(res);
    }
}

划分字母区间

在这里插入图片描述
在这里插入图片描述

class Solution {
    public List<Integer> partitionLabels(String s) {
        List<Integer>list=new LinkedList<>();
        char[]chars=s.toCharArray();
        int []edge=new int [26];
        for(int i=0;i<chars.length;i++){
            edge[chars[i]-'a']=i;//记录每个字符出现的最远位置
        }
        int ed=0;
        int st=-1;
        for(int i=0;i<chars.length;i++){
            ed=Math.max(ed,edge[chars[i]-'a']);//字符数组中距离最远的字符
            if(ed==i){
                list.add(i-st);//截取长度
                st=i;
            }
        }
        return list;
    }
}

区间合并

在这里插入图片描述
按照左端点排序,如果前后区间存在重复,则更新当前区间的右端的最大值,不重复直接加入答案

class Solution {
    public int[][] merge(int[][] intervals) {
        LinkedList<int[]>res=new LinkedList<>();
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));//左端点排序
        res.add(intervals[0]);//加入第一个区间
        for(int i=1;i<intervals.length;i++){
            if(intervals[i][0]<=res.getLast()[1]){//重叠
                int start=res.getLast()[0];
                int end=Math.max(res.getLast()[1],intervals[i][1]);//取相邻两个区间右端最大值
                res.removeLast();//移除前一个
                res.add(new int[]{start,end});//加入合并后的区间
            }else{
                res.add(intervals[i]);
            }
        }  
        return res.toArray(new int[res.size()][]);
    }
}

重置更新问题

最大连续子数组的和

在这里插入图片描述

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length==1)return nums[0];
        int res=Integer.MIN_VALUE;    //更新区间和最大值
        int count=0;    //记录区间和
        for(int i=0;i<nums.length;i++){
            count+=nums[i];
            res=Math.max(res,count);
            if(count<=0){
                count=0;    //重计区间和,跳到下一个以正数开头的区间
            }
        }
        return res;
    }
}

加油站

在这里插入图片描述
记录连续的差的和,一旦小于0,就移动起点

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int curSum=0;
        int totalSum=0;
        int start=0;
        for(int i=0;i<gas.length;i++){
            curSum+=gas[i]-cost[i];//连续差的和
            totalSum+=gas[i]-cost[i];
            if(curSum<0){
                curSum=0;//重置
                start=i+1;//跳跃
            }
        }
        if(totalSum<0)return -1;
        return start;
    }
}

股票问题

最大利润

在这里插入图片描述

//累计每一天的正利润,就保证了整体利润最大
class Solution {
    public int maxProfit(int[] prices) {
        int res=0;
        for(int i=0;i<prices.length-1;i++){
            if(prices[i+1]>prices[i]){
                res+=Math.max(prices[i+1]-prices[i],0);
            }
        }
        return res;
    }
}

含手续费

在这里插入图片描述

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int buy=prices[0]+fee;
        int res=0;
        for(int p:prices){
            if(p+fee<buy){//低价格买入
                buy=p+fee;
            }else if(p>buy){//高价格卖出
                res+=p-buy;
                buy=p;
            }
        }
        return res;
    }
}

跳跃游戏

跳跃游戏|

在这里插入图片描述

class Solution {
    public boolean canJump(int[] nums) {
        if(nums.length==1)return true;
        int cover=0;
        for(int i=0;i<=cover;i++){
            //跟新每一点的最大覆盖范围
            cover=Math.max(cover,i+nums[i]);
            if(cover>=nums.length-1){
                return true;
            }
        }
        return false;
    }
}

跳跃游戏||

在这里插入图片描述
起点~倒数第二个点之间,每当到达最远点i==nextdistance,次数增加

class Solution {
    public int jump(int[] nums) {
        int curdistance=0;//当前点最大覆盖范围
        int nextdistance=0;//下一个点最大覆盖范围
        int ans=0;
        for(int i=0;i<nums.length-1;i++){
            nextdistance=Math.max(i+nums[i],nextdistance);
            if(curdistance==i){//一到最远点就++
                ans++;
                curdistance=nextdistance;
            }
        }
        return ans;
    }
}

排序问题

分发糖果的最少数量

在这里插入图片描述
从左往右,右孩子大于左孩子,让右孩子糖果数比左孩子多一个
从右往左,左孩子大于右孩子,让左孩子糖果数要么比右孩子多一个,要么不变

class Solution {
    public int candy(int[] ratings) {
        int len=ratings.length;
        int[]candy=new int[len];
        for(int i=0;i<len;i++)candy[i]=1;//初始化为1个
        for(int i=1;i<len;i++){//从左往右遍历
            if(ratings[i]>ratings[i-1])
                candy[i]=candy[i-1]+1;//右边大的统一比左边多一个
        }
        for(int i=len-2;i>=0;i--){//从右往左遍历
            if(ratings[i]>ratings[i+1])
                candy[i]=Math.max(candy[i],candy[i+1]+1);//取最值
        }
        int sum=0;
        for(int c:candy){
            sum+=c;
        }
        return sum;
    }
}

根据身高重建队列

在这里插入图片描述
1)按照身高从高到低排序,身高一样按照k小的排前面
2)从前往后按照k值插入指定的下标位置

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        //按照身高从高到低排序,身高一样,k小的排前面
        Arrays.sort(people,(a,b)->{
            if(a[0]==b[0])return a[1]-b[1];
            return b[0]-a[0];
        });
        LinkedList<int[]>que=new LinkedList<>();
        for(int[]q:people){
            que.add(q[1],q);//将q插入到q[1]的下标位置
        }
        return que.toArray(new int[people.length][]);
    }
}

打水问题

在这里插入图片描述
最慢的人最后打水!,排序后求和即可

import java.util.*;
public class Main{
    public static void main(String[]args)throws Exception{
        Scanner sc=new Scanner(System.in);
        int N=100010;
        int[]t=new int[N];
        int n=sc.nextInt();
    
        for(int i=0;i<n;i++){
            t[i]=sc.nextInt();
        }
        Arrays.sort(t);
        long res=0;
        for(int i=0;i<n;i++){
            res+=t[i]*(n-i-1);
        }
        System.out.println(res);
    }
}

哈夫曼树

有n堆果子,将果子两两合并,每次合并消耗的体力是两堆果子重量之和,求合并所有果子消耗的最少体力

每次选择质量最少的两堆果子合并,由局部最优可以推出全局最优解

import java.util.*;
public class Main{
    public static void main(String[]args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        //小根堆
        Queue<Integer>minHeap=new PriorityQueue<>();
        for(int i=0;i<n;i++){
            minHeap.add(sc.nextInt());
        }
        int res=0;
        //每次取出最轻的两堆进行合并
        while(minHeap.size()>1){
            int a=minHeap.poll();
            int b=minHeap.poll();
            res+=a+b;
            minHeap.add(a+b);
        }
        System.out.println(res);
    }
}

绝对值不等式

在这里插入图片描述
如果是两个点a,b,则a,b到他们之间的点的距离最短,就是ab的距离,同理拓展到n个点,应选取中间的点即中位数

import java.util.*;
public class Main{
    public static void main(String[]args){
        Scanner sc=new Scanner(System.in);
        int N=100010;
        int[]a=new int[N];
        int n=sc.nextInt();
        for(int i=0;i<n;i++){
            a[i]=sc.nextInt();
        }
        Arrays.sort(a,0,n);
        long res=0;
        for(int i=0;i<n;i++){
            res+=Math.abs(a[i]-a[n/2]);
        }
        System.out.println(res);
    }
}

其他

能否成功找零

在这里插入图片描述
1)收的5块,直接收下,不用找零
2)收的10块,找5块的
3)收的20块,优先找10和5块的,否则找3个5块的

class Solution {
    public boolean lemonadeChange(int[] bills) {
        int five=0,ten=0;
        for(int bill:bills){
            if(bill==5){
                five++;
            }
            if(bill==10){
                if(five<=0)return false;
                five--;
                ten++;
            }
            if(bill==20){
                if(ten>=1&&five>=1){
                    ten--;
                    five--;
                }else if(five>=3){
                    five-=3;
                }else return false;
            }
        }
        return true;
    }
}

返回小于n的单增数字

在这里插入图片描述
从后往前遍历,如果相邻不满足单增关系,则前一个–,标记后一个要从9开始取

class Solution {
    public int monotoneIncreasingDigits(int n) {
         String s=String.valueOf(n);//转化为字符串
         char[]chars=s.toCharArray();//转化为字符数组
         int start=s.length();//标记赋值为9的位置
         for(int i=s.length()-1;i>0;i--){
             if(chars[i-1]>chars[i]){
                 chars[i-1]--;
                 start=i;
             }
        }
         for(int i=start;i<s.length();i++){
             chars[i]='9';//之后全部赋值为9
         }
         return Integer.parseInt(String.valueOf(chars));
    }
}

满足孩子数量的最大值

在这里插入图片描述

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int count=0;
        int index=s.length-1;
        for(int i=g.length-1;i>=0;i--){//遍历饼干
            if(index>=0&&g[i]<=s[index]){//先满足胃口大的
                count++;
                index--;   //自减而不是双重循环
            }
        }
        return count;
    }
}

摆动序列个数

题目描述:

在这里插入图片描述


class Solution {
    public int wiggleMaxLength(int[] nums) {
       int res=1;
       int prediff=0;   //前一对儿的差值
       int curdiff=0;   //当前对儿的差值
       for(int i=0;i<nums.length-1;i++){
           curdiff=nums[i+1]-nums[i];
           if((curdiff>0&&prediff<=0)||(curdiff<0&&prediff>=0)){
               res++;
               prediff=curdiff;
           }
       }
       return res;
    }
}

k次取反后的最大数组和

在这里插入图片描述

class Solution {
    
    public int largestSumAfterKNegations(int[] nums, int k) {   
            nums = IntStream.of(nums).boxed()
             .sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1))
             .mapToInt(Integer::intValue).toArray();
           
            int len=nums.length;
             for(int i=0;i<len;i++){
                 if(nums[i]<0&&k>0){//短路求值,k必须后判断!
                     nums[i]=-nums[i];
                     k--;
                 }
             }
             if(k%2==1){
                 nums[len-1]=-nums[len-1];
             }
             int sum=0;
             for(int i=0;i<len;i++){
                 sum+=nums[i];
             }
             return sum;
    }
}

监视所有结点的最少摄像头

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int res=0;
    public int minCamera(TreeNode root){
        /**
        0表示未覆盖,1表示有摄像头,2表示已覆盖        
         */
        //空结点看作已经覆盖
        if(root==null){
            return 2;
        }
        //后序遍历
        int left=minCamera(root.left);
        int right=minCamera(root.right);
        
        //左右儿子都覆盖,则父节点没覆盖
        if(left==2&&right==2){
            return 0;
        }
        //左右儿子至少有一个没有被覆盖,则必须放摄像头
        else if(left==0||right==0){
            res++;
            return 1;
        }
        //左右儿子至少有一个放了摄像头,则已经覆盖
        else {
            return 2;
        }
    }
    public int minCameraCover(TreeNode root) {
        //先判断根节点是否已经覆盖,额外添加摄像头
        if(minCamera(root)==0){
            res++;
        }
        return res;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值