1. 贪心算法条件与思路的解题步骤
1.一次性:某个状态以后的过程不会影响以前的状态,只与当前状态有关。
2.局部最优集合能得到全局最优。
(如果某次选择会对之后选择产生影响,局部最优不能获得全局最优可以直接KO贪心思路)
第一步,判断是否属于贪心算法的情况:对一组数据加了一定的限制,希望从中选出几个数据,在满足限制值的情况下,达到最大期望值。
第二步,用简单例子检测贪心算法是否最优。
2.贪心算法的分类
依据个人经验分成了三大类
1.分饼干,跳跃游戏,环绕加油站等每次从最小的开始分配
从问题的某一初始解出发;
while (到达目标边界)
{
if(该阶段的状态满足最优条件)加入此局部最优解;
进入下一阶段;
}
return 由所有解元素组合成问题的一个可行解;
- 教师排课器,任务调度等区间覆盖的问题
选择几个不相交的区间,从左到右将[lmin, rmax]覆盖上。按照起始端从小到大对这 n 个区间排序。
每次选择:左端点跟前面已覆盖区间不重合的、尽量小的区间,这样可以让剩下的未覆盖区间尽可能的大,就可以放置更多的区间。 - 背包价值和钱币找零问题
贡献相同期望值的情况下,每次价值越大,总体的数量/重量就更少
注:求最小生成树的Prim算法和Kruskal算法都是漂亮的贪心算法。
leetcode里贪心算法的代码实现
1.简单题455分发饼干
import java.util.Arrays;
public class AssignCookie{
public static int contentChildren(int[]g,int[]s){
assert g.length>0||s.length>0;
Arrays.sort(g);
Arrays.sort(s);
//定义第i个小孩的appetite对应第j块饼干
int i=0,j=0;
//当小孩没满足完或饼干没发完
while(i<g.length&&j<s.length){
if(g[i]<=s[j])
i++;
j++;
}
//被满足的小孩个数
return i;
}
}
中等题134. 加油站
/**
*在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
* 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
* 如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
*/
public class GasStation {
/**
*一旦遇到第一个无法到达的点 i,直接更换起始点为 i+1。中间的 [1,i-1] 的点一定不是起始点
* @param gas
* @param cost
*/
public int canCompleteCircuit(int[] gas, int[] cost) {
int sum = 0;//选定起点到现在的油缸量
int total = 0;//总的加油与耗油差值
int result = 0;//选定的起点
for(int i = 0;i<gas.length;i++){
total += gas[i]-cost[i];
if(sum<0){
sum = gas[i]-cost[i];
result = i;
}
else
sum += gas[i]-cost[i];
}
//边界条件是能跑完全程
return total>=0?result:-1;
}
}
贪心算法简单题392. 判断子序列
public class isSubsequence {
/*
* 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
* 你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
*字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
*/
public boolean isSubsequence(String s, String t) {
if(0==s.length()) return true;
//母串序列索引i,子串j
int sub = 0,j = 0;
//不能用for循环是考虑到s比t长
while( sub<s.length()&&j< t.length()){
//如果不同只有母串后移
if(s.charAt(sub)==t.charAt(j))
sub++;
j++;
}
//返回结果里判别是否子串全找到了,很有技巧
return sub==s.length();
}
}
192.跳跃游戏
public class JumpGame {
public static boolean canJump(int[] nums){
assert nums.length>0;
if(nums.length==1)return true;
//数组首位为0且0不是唯一元素肯定跳不到最后
if(nums.length>1&&nums[0]==0)return false;
for (int i = 1; i < nums.length-1; i++) {
int j = i - 1;
while (nums[i] == 0) {
//前面两个if是找到j的情况,说明这个0元素是可以被到达或跳过的
// if (nums[j] >= i - j&&i==nums.length-1) break;
if (nums[j] > i - j) break;
if (j <= 0) return false;
j--;
}
}
return true;
}
//just for test
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s = input.next();
String[] arr = s.split(",");
int[] arry = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
arry[i] = Integer.parseInt(arr[i]);
}
boolean result =canJump(arry);
System.out.println(result);
}
}
- 最大子序和
import java.util.Scanner;
/**
* 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
* 输入[-2,1,-3,4,-1,2,1,-5,4];输出6;因为连续子数组 [4,-1,2,1] 的和最大,为 6。
*/
public class MaxSubarray {
public static int maxSubarray(int[] nums) {
//sum存子序和,max表示选好子序手标的某次的和
int sum = nums[0];
int max = 0;
for(int i=0;i<nums.length;i++){
//if(max+nums[i]<nums[i])
if(max<0) max = nums[i];
else max+=nums[i];
//如果不加这个结果就会子序数组首标找对,尾标可能加进没有使整体<0但变小的数
sum = Math.max(sum,max);
}
return sum;
}
//just for test
public static void main(String[] args){
Scanner input = new Scanner(System.in);
String s= input.next();
String[] arr = s.split(",");
int[] arry = new int[arr.length];
for(int i=0;i<arr.length;i++)
{
arry[i]=Integer.parseInt(arr[i]);
}
int result = maxSubarray(arry);
System.out.println(result);
}
}