(代码如有错误,烦请指正)
***贪心算法:***在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫做贪心算法。即不从整体最优上加以考虑,做出的是在某种意义上的局部最优解。
贪心算法的解题思路:首先实现一个不依靠贪心策略的解法;接着利用对数器依次对比贪心策略1、2、3等,利用实验来判断哪个贪心策略是正确的。
注:不要纠结贪心算法的证明问题
1.一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲,给你每一个项目的开始时间和结束时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行宣讲的场次最多。返回这个最多的宣讲场次。 贪心策略:哪个会议结束时间早,选哪一个。
//会议的格式
class Program{
int start;
int end;
public Program(int start,int end){
this.start=start;
this.end=end;
}
}
//使用暴力递归解法
public static int bestArrange(Program[] programs){
if(programs==null||programs.length==0){
return 0;//没有预定的会议
}
return process(programs,0,0);
}
//programs代表还没被安排的会议,done代表已经被安排的会议数,timeLine表示当前的时间
public static int process(Program[] programs,int done,int timeLine){
if(programs.length==0){
return done;
}
int max=done;
for(int i=0;i<programs.length;i++){
if(programs[i].start>=timeLine){
Program[] exceptPrograms=copyButExceptI(programs,i);
max=Math.max(max,process(exceptPrograms,done+1,programs[i].end));
}
}
return max;
}
public static Program[] copyButExceptI(Program[] programs,int i){
Program[] exceptPrograms=new Program[programs.length-1];
int index=0;
for(int k=0;k<programs.length;k++){
if(k!=i){
exceptPrograms[index]=programs[k];
index++;
}
}
return exceptPrograms;
}
//使用贪心算法
//定义比较器
public static class programCompartor implements Comparator<Program> {
@Override
public int compare(Program p1,Program p2){
return p1.end-p2.end;
}
}
public static int bestArrange2(Program[] programs){
PriorityQueue<Program> prior=new PriorityQueue<>(new programCompartor());
int timeLine=0;
for(int i=0;i<programs.length;i++){
prior.add(programs[i]);
}
int num=0;
while(!prior.isEmpty()){
Program cur=prior.poll();
if(cur.start>=timeLine){
num++;
timeLine=cur.end;
}
}
return num;
}
2.金条切割:一条金条切分成两半,需要花费和长度相同的钱。输入一个数组,返回分割花费的最小钱数。(金条的长度等于数组元素之和)
贪心策略:先把最长的切分好。
//暴力递归解决
public static int goldSplit(int[] arr){
if(arr==null||arr.length==0){
return 0;
}
return process(arr,0);
}
//arr代表还需分割的金条的长度数组,cost代表已经花费的数量
public static int process(int[] arr,int cost){
if(arr.length==1){
return cost;
}
int min=Integer.MAX_VALUE;
//暴力遍历,看看数组中的哪两个元素合并花费最小
//i<arr.length-1和i<arr.length应该是都可以,因为arr.length=1时,会直接返回
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
min=Math.min(min,process(sumArr(arr,i,j),cost+arr[i]+arr[j]));
}
}
return min;//sunArr函数相当于已经记录了之前合并金条的花费,所以直接返回min即可
}
public static int[] sumArr(int[] arr,int i,int j){
int[] newArr=new int[arr.length-1];
int index=0;
for(int k=0;k<arr.length;k++){
if(k!=i && k!=j){
newArr[index]=arr[k];
index++;
}
}
newArr[index]=arr[i]+arr[j];//该次合并金条的花费
return newArr;
}
//使用贪心算法(数组排序太麻烦了,不如使用小根堆)
public static int goldSplit2(int[] arr){
PriorityQueue<Integer> prior=new PriorityQueue<Integer>();
for(int i=0;i<arr.length;i++){
prior.add(arr[i]);
}
int cost=0;
while(prior.size()>1){
int a=prior.poll();
int b=prior.poll();
int sum=a+b;
cost+=sum;
prior.add(sum);
}
return cost;
}
3.做项目问题:有初始资金和能做的项目个数限制,给定一批项目的花费和利润,求最后能获得的最大钱数。
贪心策略:在能做的项目中,选利润最大的一个项目做。
public static class Program{
int cost;//做该项目需要花费的钱
int get;//做该项目可以得到的利润
public Program(int cost,int get){
this.cost=cost;
this.get=get;
}
}
//programs是所有的项目信息,startMoney是初始启动资金,num是最多能做的项目个数
public static int maxMoney(Program[] programs,int startMoney,int num){
PriorityQueue<Program> costPrior=new PriorityQueue<>(new costCompartor());
PriorityQueue<Program> getPrior=new PriorityQueue<>(new getCompartor());
//把所有的项目按照cost放入小根堆中
for(int i=0;i<programs.length;i++){
costPrior.add(programs[i]);
}
int done=0;//用来记录一共做了多少个项目
while(done<num){
while(!costPrior.isEmpty() && costPrior.peek().cost<=startMoney){
getPrior.add(costPrior.poll());//将所有可以做的项目放入到大根堆中
}
if(getPrior.isEmpty()){
return startMoney;//大根堆为空,代表当前没有可做的任务,直接返回当前手头的资金即可
}
startMoney+=getPrior.poll().get;//做大根堆中利润最大的项目,并获得相应的利润
done+=1;//记录目前一共做了多少个项目
}
return startMoney;//做完num个项目后,返回所得的资金
}
//重写比较方法,按照cost排成小根堆
public static class costCompartor implements Comparator<Program>{
@Override
public int compare(Program p1,Program p2){
return p1.cost- p2.cost;
}
}
//重写比较方法,按照获得的利润排成大根堆
public static class getCompartor implements Comparator<Program>{
@Override
public int compare(Program p1,Program p2){
return p2.get-p1.get;
}
}
4.N皇后问题:在N*N的棋盘上拜N个皇后,任意两个皇后不同行、不同列、不在同一条斜线上,返回有多少种摆法。
暴力递归
//n皇后问题
public class Five {
public static int nQueenNum(int n){
if(n<1){
return 0;
}
int[] record=new int[n];
return process(0,record,n);
}
//i表示当前的行数,record用来记录皇后放置的位置,n表示一共有多少行
public static int process(int i, int[] record,int n){
if(i==n){
return 1;//到了最后一行,只能有一种可能性
}
int sum=0;
//j表示皇后位于第i行第j列
for(int j=0;j<n;j++){
if(isValid(record,i,j)){
record[i]=j;
sum+=process(i+1,record,n);
}
}
return sum;
}
public static Boolean isValid(int[] record,int i,int j){
for(int k=0;k<i;k++){
if(j==record[k] || Math.abs(record[k]-j)==Math.abs(k-i)){
return false;
}
}
return true;
}