IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 6.动态规划--背包问题和贪心问题 -> 正文阅读

[数据结构与算法]6.动态规划--背包问题和贪心问题

0-1背包问题

dp[i][w]表示,对于前i个物品,当背包容量为w的时候,所能获得的最大价值

对于是否装第i个物品.有两个状态:

如果不装入第i个物品,dp[i][w]=dp[i-1][w]

如果装入第i个物品,dp[i][w]=dp[i-1][w-wt[i-1]]+val[i-1]

子集背包问题

416--分割等和子集

?转化为背包问题,对数组进行求和,如果能分成等和子集,那么就是求是否能用他的元素凑成sum/2

  public boolean canPartition(int[] nums) {
        int sum=0;
        for (int num : nums) {
            sum+=num;
        }
        if(sum%2!=0)return false;
        int target=sum/2;
        int n=nums.length;
        boolean[][] dp = new boolean[n+1][target+1];
        for (int i = 0; i <=n ; i++) {
            dp[i][0]=true;//当背包没有空间的时候就相当于装满了
        }
        for (int i = 1; i <=n; i++) {
            for (int j = 1; j <= target; j++) {
                //背包的容量不足
                if(j-nums[i-1]<0){
                    dp[i][j]=dp[i-1][j];
                }else{
                    //如果不装入背包的情况已经为true了,那么认为它自己也是true
                    //也可以理解为放入背包和不放入背包的两种情况之和    
                    dp[i][j]=dp[i-1][j-nums[i-1]]||dp[i-1][j];
                }
            }
        }
        return dp[n][target];
    }

对该代码进行压缩处理:唯一需要注意的是 j 应该从后往前反向遍历,因为每个物品(或者说数字)只能用一次,以免之前的结果影响其他的结果

 public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (sum % 2 != 0) return false;
        int target = sum / 2;
        int n = nums.length;
        boolean[] dp = new boolean[target + 1];
        //base
        dp[0]=true;
        for (int i = 1; i <=n ; i++) {
            for (int j = target; j >= 0; j--)
                if (j - nums[i-1] >= 0)
                    dp[j] = dp[j]||dp[j - nums[i-1]];

        }
        return dp[target];
    }

518--零钱兑换

?base case 为 dp[0][..] = 0, dp[..][0] = 1。因为如果不使用任何硬币面值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是唯一的一种凑法。

如果你不把这第 i 个物品装入背包,也就是说你不使用 coins[i] 这个面值的硬币,那么凑出面额 j 的方法数 dp[i][j] 应该等于 dp[i-1][j],继承之前的结果。

如果你把这第 i 个物品装入了背包,也就是说你使用 coins[i] 这个面值的硬币,那么 dp[i][j] 应该等于 dp[i][j-coins[i-1]]

dp[i][j-coins[i-1]] 表示如果你决定使用这个面值的硬币,那么就应该关注如何凑出金额 j - coins[i-1]

优化前

 public int change(int amount, int[] coins) {
        int n=coins.length;
        int[][] dp = new int[n+1][amount+1];
        for (int i = 0; i <=amount ; i++) {
            dp[0][i]=0;
        }
        for (int i = 0; i <=n; i++) {
            dp[i][0]=1;
        }
        for (int i = 1; i <=n ; i++) {
            for (int j = 1; j <=amount ; j++) {
                if(j-coins[i-1]<0){
                    dp[i][j]=dp[i-1][j];
                }else{
                    //放入背包或者不放入背包
                    dp[i][j]=dp[i][j-coins[i-1]]+dp[i-1][j];
                }
            }
        }
        return dp[n][amount];
    }

压缩后

 public int change(int amount, int[] coins) {
        int n=coins.length;
        int[] dp = new int[amount+1];
        dp[0]=1;
        for (int i = 1; i <=n ; i++) {
            for (int j=1; j<=amount ; j++) {
                if(j-coins[i-1]>=0){
                    dp[j]=dp[j]+dp[j-coins[i-1]];
                }
            }
        }
        return dp[amount];
    }

贪心算法?

435--无重叠区间

按照区间的end进行排序,从最小的开始作为x,以end为基准,找到start比end大的区间称为下一个x,start比end小就直接删除

 public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->{
            return a[1]-b[1];
        });
        //表示最后不相交的区间数
        int res=1;//初始的时候就已经存在一个区间了
        int now=intervals[0][1];
        for (int i = 1; i < intervals.length; i++) {
            if(intervals[i][0]>=now){
                now=intervals[i][1];
                res++;
            }
        }
        return intervals.length-res;
    }

?452--重叠子区间问题,射气球

?需要注意的是,这题的范围和上题不同,在排序的时候直接相减是又可能导致整形溢出的

public int findMinArrowShots(int[][] points) {
        Arrays.sort(points,(p1,p2)->{
            return Integer.compare(p1[1],p2[1]);
        });
        int res=1;
        int now=points[0][1];
        for (int i = 1; i < points.length; i++) {
            if(points[i][0]>now){//触碰边界也会爆炸,不加=
                res++;
                now=points[i][1];
            }
        }
        return res;
    }

?253--会议室

?每次遇到红色的点,count+1,遇到绿色的点count-1,选取最少的会议室就是选择count的最大值

int minMeetingRooms(int[][] meetings) {
    int n = meetings.length;
    int[] begin = new int[n];
    int[] end = new int[n];
    for(int i = 0; i < n; i++) {
        begin[i] = meetings[i][0];
        end[i] = meetings[i][1];
    }
    Arrays.sort(begin);
    Arrays.sort(end);

    // 扫描过程中的计数器
    int count = 0;
    // 双指针技巧
    int res = 0, i = 0, j = 0;
    while (i < n && j < n) {
        if (begin[i] < end[j]) {
            // 扫描到一个红点
            count++;
            i++;
        } else {
            // 扫描到一个绿点
            count--;
            j++;
        }
        // 记录扫描过程中的最大值
        res = Math.max(res, count);
    }
    
    return res;
}

?1024--视频剪辑

看到区间问题就要想到要使用起点或者终点来进行排序,对于起点的升序排列,每次选择终点走的最远的,当两者相同的时候,选择按照终点降序排列

 public int videoStitching(int[][] clips, int time) {
        int n=clips.length;
        Arrays.sort(clips, (a, b) -> {
            return a[0] == b[0] ? Integer.compare(b[1], a[1]) : Integer.compare(a[0], b[0]);
        });
        int i=0, curEnd=0,nextEnd=0,res=0;
        //当起点比currentEnd小的时候
        while (i<n&&clips[i][0]<=curEnd){
            //贪心的选择下一个视频
            while (i<n&&clips[i][0]<=curEnd){
                nextEnd=Math.max(nextEnd,clips[i][1]);//找到符合条件的最远距离
                i++;
            }
            //超出了curEnd的范围或者超过了数组最大长度
            res++;
            curEnd=nextEnd;//此时已经不符合条件了
            if(curEnd>=time)return res; //仅当超过标准时间时才返回结果
        }
        return -1;
    }

?55--跳跃游戏

 public boolean canJump(int[] nums) {
        int n = nums.length;
        int farthest = 0;
        for (int i = 0; i < n - 1; i++) {
            // 不断计算能跳到的最远距离
            farthest = Math.max(farthest, i + nums[i]);
            // 最远距离都碰到了0,那确实是跳不动了
            if (farthest == i) return false;
        }
        return farthest >= n - 1;
    }

45--跳跃游戏2

?备忘录写法:

public class likou45 {
    int[] memo;
    public int jump(int[] nums) {
        int n=nums.length;
        memo=new int[n];//到当前索引最少跳的步数
        Arrays.fill(memo,n);//相当于无限大
        return dp(nums,0);
    }
    //从索引 i 跳到最后一格,至少需要 dp(nums, i) 步
    private int dp(int[] nums, int i) {
        int n=nums.length;
        if(i>=n-1)return 0;
        if(memo[i]!=n){
            return memo[i];
        }
        int steps=nums[i];//当前可以跳跃的步数
        for (int j = 1; j <=steps ; j++) {
            int subProblem=dp(nums,i+j);
            memo[i]=Math.min(memo[i],subProblem+1);//记录子问题
        }
        return memo[i];
    }
}

贪心写法:

public int jump(int[] nums) {
       int n=nums.length;
       int end=0,farthest=0,jumps=0;
        for (int i = 0; i < n-1; i++) {
            //不断的更新最有潜力的元素
            farthest=Math.max(nums[i]+i,farthest);
            //end表示每次能跳跃的最远索引
            if(end==i){
                //到达最远索引处,此时无论如何我们都要跳一步了
                jumps++;
                //更新下一次截止距离为最有潜力的元素能跳到的最远索引值
                end=farthest;
            }
        }
        return jumps;
    }

134--加油站

对于gas[i]-cost[i]这样一个数组,它的累加值可能如下图,只要把最低点作为起始点,就可以保证一定可以使得整个线段都在x轴之上,也就是不会空油

    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n= gas.length;
        int min=0,start=0, sum=0;
        for (int i = 0; i < n; i++) {
            sum+=gas[i]-cost[i];
            //sum成为新低
            if(sum<min){
                start=i+1;//经过了i后,sum达到最低,那么i+1就应该成为最新的起点
                min=sum;
            }
        }
        //总油量小于总消耗的时候是不能走成循环的
        if(sum<0)return -1;
        //因为是环形,所以到最后一个索引的时候,应该变成1
        return start==n?0:start;
    }
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-09 16:31:37  更:2021-10-09 16:33:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 13:20:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码