贪心策略+暴力递归+动态规划

贪心策略+暴力递归+动态规划

  1. 切金条问题
  2. 项目资金最大化问题
  3. 会议室占用
  4. 介绍递归
  5. 动态规划
    (from左神算法初级班第七节)

1.切金条问题

【问题】一块金条切成两半,是需要花费和长度数值一样的铜板的。比如 长度为20的 金条,不管切成长度多大的两半,都要花费20个铜 板。一群人想整分整块金条,怎么分最省铜板?

例:给定数组{10,20,30},代表一共三个人,整块金条长度为10+20+30=60,金条要分成10,20,30三个部分。如果,先把长度60的金条分成10和50,花费60 再把长度50的金条分成20和30,花费50 一共花费110铜板。但是如果,先把长度60的金条分成30和30,花费60再把长度30金条分成10和20,花费30一共花费90铜板。

1)哈夫曼编码问题
生成树的非叶节点之和代价最小
在这里插入图片描述
2)方法:

  • 每次将数组放入到小跟堆中,每次取出两个堆顶的值(都是最小值),不放回
  • 将两个值相加然后再放入到数组中,直到数组最后剩下最后一个数,就是生成哈夫曼树的代价。
    3)代码
public static int lessMoney(int[] arr) {
		PriorityQueue<Integer> pQ = new PriorityQueue<>();
		for (int i = 0; i < arr.length; i++) {
			pQ.add(arr[i]);
		}
		int sum = 0;
		int cur = 0;
		while (pQ.size() > 1) {
			cur = pQ.poll() + pQ.poll();
			sum += cur;
			pQ.add(cur);
		}
		return sum;
	}

2.项目资金最大化问题

给定两个数组利润数组和花费数组,以及启动资金变量w。
只有当w大于花费数组中的数值时,才能做对应的项目。求n个项目后,资金最大化是多少?一次只能做一个项目。

例如:数组0位置需要花费100,1位置需要花费150,启动资金w为120,所以目前只能做0号项目,不能做1号项目。

1)贪心策略
使用一个大根堆和一个小根堆

  • 将项目信息(以花费作为排序)放入小根堆中,与启动资金w比较,项目的花费比w小项目就弹出,全部放入到大根堆中
  • 然后每次从大根堆里取最大值。
  • 项目完成后,用新的w跟小根堆里花费进行比较,看看能不能够弹出新的项目。有就更新大根堆。
    在这里插入图片描述
    在这里插入图片描述
    2)代码
import java.util.Comparator;
import java.util.PriorityQueue;

public class Code_03_IPO {
	public static class Node {
		public int p;//收益
		public int c;//花费

		public Node(int p, int c) {
			this.p = p;
			this.c = c;
		}
	}

	public static class MinCostComparator implements Comparator<Node> {

		@Override
		public int compare(Node o1, Node o2) {
			return o1.c - o2.c;
		}

	}

	public static class MaxProfitComparator implements Comparator<Node> {

		@Override
		public int compare(Node o1, Node o2) {//按照收益
			return o2.p - o1.p;
		}

	}

	public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
		Node[] nodes = new Node[Profits.length];
		for (int i = 0; i < Profits.length; i++) {//生成Node
			nodes[i] = new Node(Profits[i], Capital[i]);
		}

		PriorityQueue<Node> minCostQ = new PriorityQueue<>(new MinCostComparator());//代价小根堆
		PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());//利益大根堆
		for (int i = 0; i < nodes.length; i++) {//把所有项目加到小根堆中去
			minCostQ.add(nodes[i]);
		}
		for (int i = 0; i < k; i++) {//最多做k个项目
			while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {//如果堆顶的项目花费小于w
				maxProfitQ.add(minCostQ.poll());
			}
			if (maxProfitQ.isEmpty()) {//没做到k个项目就得停,因为没有项目可以做了
				return W;
			}
			W += maxProfitQ.poll().p;
		}
		return W;
	}

}

3.会议室占用问题

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

1)贪心策略
按照最早结束时间安排项目
2)代码:

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

public class Code_06_BestArrange {

	public static class Program {
		public int start;
		public int end;

		public Program(int start, int end) {
			this.start = start;
			this.end = end;
		}
	}

	public static class ProgramComparator implements Comparator<Program> {

		@Override
		public int compare(Program o1, Program o2) {//谁结束早,就选谁
			return o1.end - o2.end;
		}

	}

	public static int bestArrange(Program[] programs, int start) {
		Arrays.sort(programs, new ProgramComparator());//
		int result = 0;
		for (int i = 0; i < programs.length; i++) {
			if (start <= programs[i].start) {
				result++;
				start = programs[i].end;
			}
		}
		return result;
	}

}

4.介绍递归

1)求n!问题
代码:

public class Code_01_Factorial {
	public static long getFactorial1(int n) {
		if (n == 1) {
			return 1L;
		}
		return (long) n * getFactorial1(n - 1);
	}

	public static long getFactorial2(int n) {
		long result = 1L;
		for (int i = 1; i <= n; i++) {
			result *= i;
		}
		return result;
	}

	public static void main(String[] args) {
		int n = 5;
		System.out.println(getFactorial1(n));
		System.out.println(getFactorial2(n));
	}

}

2)汉诺塔问题

  • 1~n-1从from挪到help上去
  • n从from挪到to上去
  • 1~n-1从help挪到to上面去
    在这里插入图片描述

代码:

public static void process(int N,String from,String to,String help) {
		if(N==1) {
			System.out.println("Move 1 from"+from+"to "+to);
		}else {
			process(N-1,from,help,to);
			System.out.println("Move "+N+"from "+from+" to"+to);
			process(N-1,help,to,from);
		}
	}

3)打印一个字符串的全部子序列问题,包括空字符串
每个位置都是要还是不要

public static void printAllSub(char[] str,int i,String res) {
		if(i==str.length) {
			System.out.println(res);
			return;
		}
		printAllSub(str,i+1,res);
		printAllSub(str,i+1,res+String.valueOf(str[i]));
	}

4)母牛问题
母牛每年生一只母牛,新生的母牛成长三年后也能每年生一只母牛,假设都不会死。求N年后,母牛的数量。

  • 公式为:T(N)=T(N-1)+T(N-3)
public static int cowNumber1(int n) {
		if (n < 1) {
			return 0;
		}
		if (n == 1 || n == 2 || n == 3) {
			return n;
		}
		return cowNumber1(n - 1) + cowNumber1(n - 3);
	}

5.动态规划

1)二维数组问题
给你一个二维数组,二维数组中每个数都是正数,要求从右下角走到左上角,每一步只能向左或者向上。沿途经过的数字要累加起来。返回最小的路径和。

求解过程:
从[i,j]出发,走向左上角,要么向上走,要么向左走。分为3种情况。情况1:走到了最左边还没有到最上边。情况2:走到了最上边,还没有走到最左边。情况3:既可以往左走,也可以向上走。

  • 递归代码:
public static int minPath1(int[][] matrix) {
		return process1(matrix, matrix.length - 1, matrix[0].length - 1);
	}

	public static int process1(int[][] matrix, int i, int j) {
		int res = matrix[i][j];
		if (i == 0 && j == 0) {//到达左上角
			return res;
		}
		if (i == 0 && j != 0) {//到达最左边,还没有达到最上边
			return res + process1(matrix, i, j - 1);
		}
		if (i != 0 && j == 0) {//达到最上面,还没达到最左边
			return res + process1(matrix, i - 1, j);
		}
		return res + Math.min(process1(matrix, i, j - 1), process1(matrix, i - 1, j));//普遍情况,既可以向上走,也可以向左走,找最小的
	}
  • 递归改动态规划:

递归改动态规划条件(无后效性问题):
当递归有重复状态,并且与到达路径状态没有关系

暴力递归改动态规划:
在这里插入图片描述

public static int minPath2(int[][] m) {
		if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
			return 0;
		}
		int row = m.length;
		int col = m[0].length;
		int[][] dp = new int[row][col];
		dp[0][0] = m[0][0];
		for (int i = 1; i < row; i++) {
			dp[i][0] = dp[i - 1][0] + m[i][0];
		}
		for (int j = 1; j < col; j++) {
			dp[0][j] = dp[0][j - 1] + m[0][j];
		}
		for (int i = 1; i < row; i++) {
			for (int j = 1; j < col; j++) {
				dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + m[i][j];
			}
		}
		return dp[row - 1][col - 1];
	}

2)给定一个数组arr,和一个整数aim。如果可以任意选择arr中的数字,能不能累加得到aim,返回true或者false。

递归方法还是要还是不要的问题

public static boolean isSum(int[] arr,int i,int sum,int aim) {
		if(i==arr.length) {
			return sum==aim;
		}
		return isSum(arr,i+1,sum,aim)||isSum(arr,i+1,sum+arr[i],aim);
	}

暴力递归改动态规划:
在这里插入图片描述

发布了11 篇原创文章 · 获赞 0 · 访问量 580

猜你喜欢

转载自blog.csdn.net/weixin_41585309/article/details/104054068