Bootstrap

算法day1:贪心算法—— 最优装载问题、零钱兑换、01背包问题

蓝桥杯算法合集: 蓝桥杯算法合集(终极完结版)

贪心算法:

思想
每一步都采取当前状态下的最优选择 从而希望推出全局最优解

缺点:
用贪心策略求得的不一定是全局最优解。比如换零钱那道题,若换成 25 20 5 1四种面值 按照该算法结果应为 面值25 5 5 5 1一共需要5张,显然不对 。正确结果应该为20 20 1只需要3张即可 。
因为没有遍历全部可能性解,只是当前这一步可以达到的局部最优解,过早做出决定 ,贪图眼前局部利益的最大化 。没有放眼长远未来而是走一步看一步,所以通常作为辅助策略来使用

小知识点:

  • 子序列可以不连续 但是相对位置不改变
    子串、子数组、子区间必须连续

下面两个知识点是在01背包问题时用上的

  • Article类的数组
Article [] articles=new Article[] {
				new Article(35,10),new Article(30,40),
				new Article(60,30),new Article(50,50),
				new Article(40,35),new Article(10,40),
				new Article(25,30)
		};
  • 对article数组按cmp迭代器排序
Comparator<Article> cmp=(Article a1,Article a2)->{return Double.compare(a2.valueOfDensity,a1.valueOfDensity);}
Arrays.sort(articles, cmp);

最优装载问题

问题描述
在北美洲东南部,有一片神秘的海域,那里碧海 蓝天、阳光明媚,这正是传说中海盗最活跃的加勒比海。17 世纪时,这里更是欧洲大陆 的商旅舰队到达美洲的必经之地,所以当时的海盗活 动非常猖獗。有一天,海盗们截获了一艘装满各种各样古董的 货船,每一件古董都价值连城,一旦打碎就失去了它 的价值。
虽然海盗船足够大,但载重量为 30,每件古董的重量分别为 3,5,4,10,7,14,2,11,
海盗们该如何把尽可能多数量的宝贝装上海盗船呢?
package 贪心;
import java.util.Arrays;
public class 最优装载问题 {

	public static void main(String[] args) {
		Integer[] weights= {3,5,4,10,7,14,2,11};
		//从小到大排2 3 4 5 7 10 11 14
		Arrays.sort(weights);
//		for(int i=0;i<weights.length;i++)
//			System.out.println(weights[i]);
		int capacity=30,count=0,newWeight=0;
		//当前所选古董的总重量比船容量小才进入选古董 否则提前跳出外循环
		for(int i=0;i<weights.length&&newWeight<capacity;i++) {
			//尝试将古董装上船
			newWeight=newWeight+weights[i];
			if(newWeight<=capacity) {//满足船的容量则正真装上 计数器加1
				count++;
				System.out.println(weights[i]);
			}
		}
		System.out.println(count);
	}

}

零钱兑换

问题描述
假设有25分、10分、5分、1分的硬币,
先要发给客户41分的零钱,如何办到硬币个数最少?
package 贪心;
import java.util.Arrays;

//用贪心策略求得的不一定是全局最优解  
//此题若换成 25 20 5 1四种面值 按照该算法结果应为 面值25 5 5 5 1一共需要5张
//显然不对 正确结果应该为20 20 1只需要3张即可
//因为没有遍历全部可能性解 只是当前这一步可以达到的局部最优解 过早做出决定 
//贪图眼前局部利益的最大化 没有放眼长远未来 走一步看一步 
//通常作为辅助策略来使用
public class 零钱兑换 {
	static void moneyChange(Integer[] a,int money) {
		Arrays.sort(a,(Integer a1,Integer a2)->{return a2-a1;});
		//25 10 5 1 		41
		int i=0;
		while(i<a.length) {
				if(money<a[i]) {//当剩余的钱小于该面值时 i++跳过该面值
					i++;
					continue;
				}
				//能执行到这里说明当前剩余的前大于该面值却比上一次面值小 该面值为当前最适
				money-=a[i];
				System.out.println(a[i]);
		}
	}
		static void moneyChange2(Integer[] a,int money) {
			//从小往大排:1 5 10 25
			Arrays.sort(a);
			int count=0;
			for(int i=a.length-1;i>=0;i--) {
				if(money<a[i]) {//当所有面值都比剩余的钱大时 结束循环
					continue;
				}
				money-=a[i];
				System.out.println(a[i]);
				count++;
				//选了最大面值之后 还要从最大开开始重新枚举
				i=a.length;
			}
			System.out.println(count);
		
			
		
	}
	public static void main(String[] args) {
		Integer[] faces= {5,10,25,1};
		
		//或者Arrays.sort(faces,(Integer a1,Integer a2)-> a2-a1);
		int money=41;
		//moneyChange(faces,money);
		moneyChange2(faces,money);
		
	}

}

01背包问题

问题描述:
有n件物品,每个物品都有一个大小和价值,给你一个固定容量的背包,要求装的东西价值总和最大
 实例:
 现在有重量分别为35 30 60 50 40 10 25,价值分别为10 40 30 50 35 40 30的物品,
 背包最大承重为150,求该背包所能放置的物品最大价值是多少?
package 贪心;

public class Article {
	public int weight,value;
	public double valueOfDensity;
	Article(int weight,int value){
		this.weight=weight;
		this.value=value;
		this.valueOfDensity=value*1.0/weight;
	}
	@Override
	public String toString() {
		return "Article [weight=" + weight + ", value=" + value + ", valueOfDensity=" + valueOfDensity + "]";
	}
}

package 贪心;


import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class _01背包问题 {
	//传一个比较器Comparator<Article> cmp 按设定关键字排序
	static void select(String title,Comparator<Article> cmp) {
		//将物品变量直接封装在成员方法里作为局部变量 就可以不用通过mian方法传参了
		Article [] articles=new Article[] {
				new Article(35,10),new Article(30,40),
				new Article(60,30),new Article(50,50),
				new Article(40,35),new Article(10,40),
				new Article(25,30)
		};
		Arrays.sort(articles, cmp);
		//将Article类封装到list存储结构
		List<Article> selectedArticles=new LinkedList<>();
		int capacity=150,weight=0,value=0;
		for(int i=0;i<articles.length&&weight<capacity;i++) {
			int newWeight=weight+articles[i].weight;
			if(newWeight<=capacity) {
				weight=newWeight;
				value+=articles[i].value;
				selectedArticles.add(articles[i]);
			}
		}
		for(int i=0;i<selectedArticles.size();i++) {
			System.out.println(selectedArticles.get(i));
		}
		System.out.println(weight);
		System.out.println(value);
		
	}
	public static void main(String[] args) {
		//价值主导
		select("价值主导",(Article a1,Article a2)->{	
			return a2.value-a1.value;
		});
		//重量主导
		select("重量主导",(Article a1,Article a2)->{	
					return a1.weight-a2.weight;
				});
		//价值主导
		//a2.valueOfDensity-a1.valueOfDensity;结果为double型 而迭代器要求返回类型为int
		//Double.compare(double d1,doouble d2)返回int型
		select("价值密度主导",(Article a1,Article a2)->{	
					return Double.compare(a2.valueOfDensity, a1.valueOfDensity);
				});
		
	}

}

使用贪心算法由于其目光短浅,算出来不一定正确,01背包这题用动态规划解,详见:动态规划常见模型之背包专题

;