博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA算法:贪心算法典型题目详解 (JAVA版本 共6题)
阅读量:4041 次
发布时间:2019-05-24

本文共 8638 字,大约阅读时间需要 28 分钟。

JAVA算法:贪心算法典型题目详解(JAVA版本)

题目一:最优装载问题

一条小船用来运输古董到河对岸。假设船的最大载重量为MAXWEIGHT,每件古董的重量为w_i,怎么能够装载最多数量的古董到船上呢?

样例数据:

MAXWEIGHT 为 30

给定8个古董,重量分别为:[4, 10, 7, 11, 3, 5, 14, 2]

算法分析

这个问题是一个典型的可以使用贪心算法解决的问题。

通过分析题目可以看到,小船的载重量(MAXWEIGHT)是固定的,要求装载的物品数量最多,那么应该优先选择把重量最小的物品装上船,然后不断装载剩余的物品,直到达到小船载重量的要求。

选择先装载重量最小的物品,这个选择策略就采用了贪心(Greedy)策略,从局部最优达到全局最优,从而产生最优装载问题的最优解。

算法设计与实现

package com.bean.algorithm.learning;import java.util.Arrays;public class OptimizedLoading {	public static int MAXWEIGHT = 30;// 小船的载重量	public static int AMOUNT = 8;// 古董个数	/*	 * 装载算法(贪心算法)	 * */	public static int maxLoading(int[] weight) {		//计数器,用于记录装载到小船上古董的数量		int counter = 0;		// 对weight数组进行排序		Arrays.sort(weight);		int temp = 0; // 记录装载到船上的古董重量		for (int i = 0; i < AMOUNT; i++) {			temp += weight[i]; // 贪心策略:每次装入最轻者			if (temp <= MAXWEIGHT) // 若加入最轻者后还小于载重量,则古董个数+1				counter++;			else				//超重,不能装载				break;		}        //返回装载的古董的数量		return counter;	}	public static void main(String[] args) {		int ANSWER = 0; // 记录已装载的古董个数		int[] weight = { 4, 10, 7, 11, 3, 5, 14, 2 };		ANSWER = maxLoading(weight);		System.out.println("能装入的古董最大数量为: " + ANSWER);	}}

程序运行结果:

能装入的古董最大数量为: 5


题目二:活动安排

设有n个活动的集合e={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间s_i和一个结束时间f_i,且s_i< f_i。如果选择了活动i,则它在半开时间区间[s_i,f_i]内占用资源。若区间[s_i,f_i]与区间[s_j,f_j]不相交,则称活动 i 与活动 j 是相容的。也就是说,当s_i ≥ f_i或s_j ≥ f_j时,活动 i 与活动 j 相容。活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合。

在下面所给出的解活动安排问题的贪心算法gpeedyselector中,各活动的起始时间和结束时间存储于数组s和f{中且按结束时间的非减序:.f_1 ≤ f_2 ≤ … ≤ f_n排列。如果所给出的活动未按此序排列,我们可以用o(nlogn)的时间将它重排。

package com.bean.algorithm.beginner;import java.util.ArrayList;import java.util.List;public class ActivitiesDemo {	public static List
arrangeActivity(int[] start, int[] end) { int total = start.length; int endFlag = end[0]; List
results = new ArrayList<>(); results.add(0); for (int i = 0; i < total; i++) { if (start[i] > endFlag) { results.add(i); endFlag = end[i]; } } return results; } public static void main(String[] args) { // TODO Auto-generated method stub int[] start = { 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12 }; int[] end = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; List
results = arrangeActivity(start, end); for (int i = 0; i < results.size(); i++) { int index = results.get(i); System.out.println("开始时间:" + start[index] + ",结束时间:" + end[index]); } }}

 程序运行结果:

开始时间:1,结束时间:4

开始时间:5,结束时间:7
开始时间:8,结束时间:11
开始时间:12,结束时间:14


题目三:剪绳子

给定一根长度为n的绳子,请把绳子剪成m段,每段绳子记为k[0],k[1]……k[m]。请问k[0]*k[1]……*k[m]可能的最大乘积是多少?例如:当绳子长度为8时,我们把它剪成长度分别为2,3,3段,此时最大乘积为18.

算法分析

从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止,这就是贪婪算法。

在剪绳子中,如果绳子的长度大于5,则每次剪出的长度为3的绳子。如果剩下的长度仍然大于5,则接着剪出一段长度为3的绳子,重复这个步骤,直到剩下的长度小于5.时间和空间复杂度都为O(1)。

 

算法设计

可以参考我的另外一篇博文:

package com.bean.algorithm.basic;public class CutForMaxProduct4 {	// Greedy solution for Max Product Problem	public static int maxProd(int length) {		if (length < 2)			return 0;		if (length == 2)			return 1;		if (length == 3)			return 2;		int timesOf3 = length / 3;    //剪长度为3的绳子段		if ((length - timesOf3 * 3) == 1) // 当长度为4的时候不能去用3剪			timesOf3 -= 1;      		int timesOf2= (length - timesOf3* 3) / 2;     // 这时应该把绳子剪成长度为2的两段:2*2>1*3		return ((int)(Math.pow(3, timesOf3))*((int)Math.pow(2, timesOf2)));	}	/* Driver program to test above functions */	public static void main(String[] args) {		System.out.println("Maximum Product is " + maxProd(10));	}}

程序运行结果:

 Maximum Product is 36


题目四:机器人行走

机器人在一个无限大小的网格上行走,从点 (0, 0) 处开始出发,面向北方。该机器人可以接收以下三种类型的命令:

-2:向左转 90 度

-1:向右转 90 度
1 <= x <= 9:向前移动 x 个单位长度
在网格上有一些格子被视为障碍物。

第 i 个障碍物位于网格点  (obstacles[i][0], obstacles[i][1])

如果机器人试图走到障碍物上方,那么它将停留在障碍物的前一个网格方块上,但仍然可以继续该路线的其余部分。

返回从原点到机器人的最大欧式距离的平方。

示例 1:

输入: commands = [4,-1,3], obstacles = []

输出: 25
解释: 机器人将会到达 (3, 4)
示例 2:

输入: commands = [4,-1,4,-2,4], obstacles = [[2,4]]

输出: 65
解释: 机器人在左转走到 (1, 8) 之前将被困在 (1, 4) 处

提示:

0 <= commands.length <= 10000

0 <= obstacles.length <= 10000
-30000 <= obstacle[i][0] <= 30000
-30000 <= obstacle[i][1] <= 30000
答案保证小于 2 ^ 31

算法分析

 

算法设计

class Solution {    public int robotSim(int[] commands, int[][] obstacles) {        if(commands == null || commands.length == 0)            return 0;        Set
obstacleSet = new HashSet<>(); for(int[] o : obstacles) obstacleSet.add(o[0] + "," + o[1]); int maxVal = 0, direction = 0, x = 0, y = 0; int[][] directions = {
{0,1},{1,0},{0,-1},{-1,0}}; for(int command : commands){ if(command == -1){ direction = (direction + 1) % 4; }else if(command == -2){ direction = (direction - 1) % 4; if(direction < 0) direction += 4; }else if(command >= 1 && command <= 9){ for(int i=0; i

另外一种算法设计

class Solution {    public int robotSim(int[] commands, int[][] obstacles) {        int[][] walk = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };		Map
map = new HashMap<>(); for (int i = 0; i < obstacles.length; i++) map.put(obstacles[i][0] + "," + obstacles[i][1], true); int p = 0, q = 0, k = 0; int max = 0; for (int command : commands) { if (command == -1) k = (k + 1) % 4; else if (command == -2) k = (k - 1 + 4) % 4; else { for (int i = 0; i < command; i++) { if (map.containsKey((p + walk[k][0]) + "," + (q + walk[k][1]))) break; p += walk[k][0]; q += walk[k][1]; max = Math.max(max, p * p + q * q); } } } return max; }}

题目五:任务调度问题

给定一个用字符数组表示的 CPU 需要执行的任务列表。其中包含使用大写的 A - Z 字母表示的26种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。

然而,两个相同种类的任务之间必须有长度为 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

你需要计算完成所有任务所需要的最短时间。

示例 1:

输入: tasks = ["A","A","A","B","B","B"], n = 2

输出: 8
执行顺序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B.
注:

任务的总个数为 [1, 10000]

n 的取值范围为 [0, 100]

算法分析

 

算法设计

package com.bean.algorithm.learning;import java.util.Arrays;public class TaskScheduler {	public int leastInterval(char[] tasks, int n) {		// count char freq, task name doesn't matter, only freq matters		int[] freq = new int[26];		for (char c : tasks)			freq[c - 'A']++;		// sort first, so we have max freq at freq[25]		Arrays.sort(freq);		int time = 0;		while (freq[25] > 0) { // while we still have task to do, start from most freq task			// GREEDY			// each round/row, try to finish n tasks			for (int i = 0, p = 25; i <= n; i++) { // n is the cooling down factor, p points to the next task to consume				if (p >= 0 && freq[p] > 0) { // if there is still task to do					freq[p]--; // do task					p--; // move p to next freq task					time++; // take one cycle				} else if (freq[25] != 0) { // if this is NOT last row, need to fill in idle cycle					time++; // take one cycle				} // else freq[25] == 0 . no more task to do and last row. we WON'T fill in idle					// cycle			}			// sort again so next round we're going to start from most freq task and consume			// n task if possible			Arrays.sort(freq);		}		return time;	}	public static void main(String[] args) {		// TODO Auto-generated method stub		TaskScheduler taskScheduler=new TaskScheduler();				char[] tasks= {'A','A','A','B','B','B'};		int n=2;				int result=-1;				result=taskScheduler.leastInterval(tasks, n);		System.out.println("result = "+result);					}}

程序运行结果:

result = 8


题目六:重构字符串

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:

输入: S = "aab"

输出: "aba"
示例 2:

输入: S = "aaab"

输出: ""
注意:

S 只包含小写字母并且长度在[1, 500]区间内。

算法分析

给定一个字符串,挪动给定的字符串中字符的位置,使得返回的字符串需要相邻的两个字符不能相同。

思路:

  • 首先将给定的字符串按照字符存储在一个int的数组中,数组的下标为字符串中字符-‘a’,所以该数组将相同的字符的个数存储在数组中。
  • 利用maxLength获得数组中元素最大的值,即字符串中重复出现的字符最多的字符个数,如果该个数 * 2 - 1大于字符串的长度,就说明相同的字符太多,其他字符已经不能将相同字符分割开
  • 将字符串中的字符按照奇数偶数放在新建的char数组中。将相同的字符个数小于字符串长度的一半的字符放在奇数下标位置,否则放在偶数下标位置。注意这里需要判断奇数位置是否大于字符串长度

算法设计

class Solution {    public String reorganizeString(String S) {        int[] arr = new int[26];        int lenght = S.length();        if(S.length() == 1) return S;        char[] ret = new char[lenght];        int maxLength = 0;        for(char a: S.toCharArray()) {            if(maxLength < ++arr[a - 'a'])                maxLength = arr[a - 'a'];        }        if(maxLength * 2 - 1 > S.length())            return "";        int odd = 0, even = 1;        for(int i = 0; i < 26; i++) {                while(arr[i] > 0 && arr[i] < lenght / 2 + 1 && even < lenght) {                    ret[even] = (char)(i + 'a');                    arr[i] --;                    even += 2;                }                while(arr[i] > 0) {                    ret[odd] = (char)(i + 'a');                    arr[i] --;                    odd += 2;                }        }        return new String(ret);       }}

 


 

转载地址:http://xntdi.baihongyu.com/

你可能感兴趣的文章
js弹窗插件
查看>>
自定义 select 下拉框 多选插件
查看>>
js判断数组内是否有重复值
查看>>
js获取url链接携带的参数值
查看>>
gdb 调试core dump
查看>>
gdb debug tips
查看>>
arm linux 生成火焰图
查看>>
linux和windows内存布局验证
查看>>
linux config
查看>>
linux insmod error -1 required key invalid
查看>>
linux kconfig配置
查看>>
linux不同模块completion通信
查看>>
linux printf获得时间戳
查看>>
C语言位扩展
查看>>
linux dump_backtrace
查看>>
linux irqdebug
查看>>
git 常用命令
查看>>
linux位操作API
查看>>
uboot.lds文件分析
查看>>
uboot start.s文件分析
查看>>