Algoritmo Ganancioso (Vários Exemplos Convencionais)

Algoritmo Ganancioso (Vários Exemplos Convencionais)

贪心算法,指在对问题进行求解的时候,总是做出当前看来是最好的选择。也就是说不从整体上最优上考虑,算法得到的结果是某种意义上的局部最优解


introduzir

Problemas que podem ser resolvidos com um algoritmo guloso têm as seguintes características

  • 1.贪心选择的性质:一个问题的整体最优解可以通过一系列局部的最优解的选择达到。并且每一次的选择可以依赖于之前做出的选择,但是不依赖后面做出的选择。这就是贪心选择性质。对于一个具体的问题,要确定他是否具有贪心选择的性质,必须证明每一步所作的贪心选择最终导致问题的整体的最优解

  • 2.最优子结构性质:当一个恩问题的最优解包含其子问题的最优解的时候,此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法的求解所在。

Etapas para uso

As etapas básicas do uso do algoritmo guloso:
   1> Estabelecer um modelo matemático para descrever o problema.
   2> Divida o problema a ser resolvido em vários subproblemas.
   3> Resolva cada subproblema para obter a solução ótima local do subproblema.
   4> Combine a solução ótima local do subproblema em uma solução do problema original.

实际上在使用贪心算法的时候,待选

modelo

while(约束条件成立)
{
    选择当前最优解,记录并累计到最后的解中
        if(当前最优解使用完毕)
    	当前最优解=当前次优解;
}

Desvantagens dos algoritmos gananciosos

Os seguintes defeitos

1.不能保证解是最佳的。因为贪心算法得到的是局部最优解,不是从整体开了整体最优解。

2.贪心算法只能确定某些问题的可行性范围

Os defeitos 1 e 2 significam que o algoritmo ganancioso tem limitações. Por exemplo, se você trocar moedas, se for 8 yuans, haverá 2 yuans e 5 yuans, então 5 yuans por uma peça, 2 yuans por uma peça e 1 yuan esquerda. Então não pode ser calculado. Na verdade, quatro 2 yuans podem ser usados. Este é um exemplo

3.贪心算法一般用来解决最大或者最小解

O defeito é que o algoritmo guloso usa uma determinada estratégia para selecionar um valor ao invés de percorrer todas as soluções.

exemplo clássico

1. Problema de seleção de atividades

2. Problema de troca de moeda

3. Revisitando o problema da mochila

4. O problema da travessia de barco no rio

5. Problema de cobertura de intervalo

exemplo comum

Perguntas de seleção de atividades

Este problema geralmente pode ser resolvido por algoritmo guloso e programação dinâmica. Usaremos o algoritmo guloso para resolvê-lo abaixo.
A forma da pergunta é: para realizar várias atividades em um determinado local, e cada atividade terá um tempo diferente (diferentes horários de início e término), como organizar o maior número possível de atividades? A razão pela qual este problema pode ser resolvido com um algoritmo guloso é que a escolha da próxima atividade só pode depender do prazo da atividade anterior, e não é necessário considerar a situação geral

Atividade 1 2 3 4 5 6 7 8 9 10 11
Hora de início 1 3 0 5 3 5 6 8 8 2 12
Fim do tempo 4 5 6 7 8 9 10 11 12 13 14

Ao resolver este problema, é necessário classificar o horário de término, pois o horário de término da atividade anterior afetará a seleção da próxima atividade, e o horário de início da atividade atual não afetará a seleção da próxima atividade, portanto basta olhar para o horário de término. Bem, use o horário de término da atividade anterior para comparar com o horário de início da próxima atividade, se o primeiro for menor que o último, então o último é a próxima atividade, caso contrário, continue a comparar o hora de início da próxima atividade

ideias para resolução de problemas

  1. 将所有活动按照结束时间进行排序
  2. 然后默认选取第一个活动,用变量endTime标注当前活动的结束时间,然后与后面活动的开始时间进行比较
  3. 如果前者结束时间小于等于后者活动开始时间,那么选择后者这个活动,反之,继续比较下下一个活动,再次进行判断
  4. 重复进行第三步,直到所有活动比较完成
import java.util.*;

/**
 * 活动选择:
 * 大概形式为:在某一个地方,有多个活动要进行,活动有开始时间和结束时间,我们该如何选择才能在这一天中这一个地点,举办最多的活动
 * 求解最多的活动个数为?
 */
class Play{
    
    
    int preTime;
    int endTime;//表示活动的开始时间和结束时间
}
public class 活动选择 {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner=new Scanner(System.in);
        //我们可以用容器来存储各个活动,然后进行贪心算法,最后得到结果
        //我们可以用链表来进行存储,排序
        LinkedList<Play> linkedList=new LinkedList();
        int n=scanner.nextInt();//表示活动的个数
        for (int i = 0; i <n ; i++) {
    
    
            //提案写每一个活动的开始时间和结束时间
            Play play=new Play();
            play.preTime=scanner.nextInt();
            play.endTime=scanner.nextInt();
            linkedList.add(play);//存储链表中,然后进行对于结束时间排序
        }
        Collections.sort(linkedList, new Comparator<Play>() {
    
    
            @Override
            public int compare(Play o1, Play o2) {
    
    
                return o1.endTime-o2.endTime;
            }
        });
        //排序完成
        //然后进行判别,从第一个活动开始
        int num=0;//计数
        Play Time=linkedList.get(0);
        for (int i = 0; i <linkedList.size() ; i++) {
    
    
            if(Time.endTime<=linkedList.get(i).preTime){
    
    
                Time=linkedList.get(i);
                num++;
            }
        }
        System.out.println(num);//得到
    }
}

problema de troca de moeda

A descrição geral do problema é: suponha que haja notas a, b, c, d, e, f de 1 yuan, 2 yuan, 5 yuan, 10 yuan, 20 yuan, 50 yuan e 100 yuan, respectivamente. Agora, para usar esse dinheiro para pagar K yuan, pelo menos quantas cédulas devem ser usadas? Este problema é um pouco semelhante ao problema anterior.Para resolvê-lo, geralmente existem duas matrizes, uma representa o tamanho da denominação e a outra representa o número de moedas de diferentes denominações, e a matriz que representa a denominação é ordenada.

Estamos resolvendo o menor número de moedas, então podemos usar o algoritmo guloso

1.理所当然先使用大额纸币,当大额纸币张数不够的时候,再使用后面较小面额的纸币,依次递减,直到表示完所有纸币

2.做题方法可以递归也可以迭代

import java.util.*;

public class 钱币找零问题 {
    
    
    public static void main(String[] args) {
    
    
        //题目:指定币值和相应的数量,用最少的数量去凑齐某金额
        //思路:利用贪心算法,我们优先选择面值大的纸币,依次类推,直到凑齐总金额
        Scanner scanner=new Scanner(System.in);
        int num=scanner.nextInt();//输入总金额
        greedy(num);
    }
    public static void greedy(int num){
    
    
        int[] values = {
    
     1, 2, 5, 10, 20, 50, 100 };
        //数量
        int[] counts = {
    
     3, 3, 2, 1, 1, 3, 3 };
        //获取需要各种面值多少张
        int[] result = getNumber(num, values, counts);
        System.out.println("各币值的数量:"+Arrays.toString(result));
    }
    public static int[] getNumber(int sum,int []values,int[]counts){
    
    
        int []result=new int[7];//表示有七种钱币
        //我们要找的是每一种钱币所需的票数
        int add=0;//表示需要的总钱数
        for (int i = values.length-1; i >=0 ; i--) {
    
    
            int num=(sum-add)/values[i];
            if(num>counts[i]){
    
    
                num=counts[i];
            }
            //add加上数值
            add=add+num*values[i];
            result[i]=num;
        }
        return result;
    }
    //或者这样迭代
    public static int[] get(int sum,int []values,int[]counts){
    
    
        int []result=new int[values.length];
        for (int i = values.length-1; i >=0 ; i++) {
    
    
            int num=Math.min(counts[i],sum/values[i]);
            result[i]=num;
            sum=sum-num*values[i];
        }
        return result;
    }
}

problema da mochila

Não 01 Mochila

常见题型为给定n种物品,一个背包,背包的容量是c,二米一个物品i的价值为vi,重量为wi,如何选择装入的物品使得背包的总价值最大?

O problema da mochila aqui se refere a uma parte da mochila, não é como o problema da mochila 01 onde um item não pode ser desmontado, os itens aqui podem ser desmontados e uma parte dele pode ser colocada na mochila. Sob a ótica do algoritmo guloso, o que garantimos é que o valor total da mochila seja o maior possível, ou seja, garantir que o valor individual de cada item colocado seja o maior possível. (pode ser desmontado)

Proceda da seguinte forma

1.需要将物品按照单位重量价值进行排序。

2.将尽可能多的单位i重量价值最高的物品装入被阿波,若最大单位重量价值的物品全部装入背包后,背包华友多余容量,则选择单位重量价值次高的尽可能多的装入背包中。

3.如果最后一件物品无法装入,那就计算可以装入的比例,然后按照比例装入

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Scanner;

/**
 * 贪心的背包 ,有n个物品,一个背包,然后想使得这个背包的价值最大
 * 不是01背包不可拆分物品,这个是可以拆分的
 */
class beiBao{
    
    
    int values;
    int weight;//价值和重量
    double wValues;//单个物品的单位重量的价值
}
public class 背包 {
    
    

    public static void main(String[] args) {
    
    
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        int weight=scanner.nextInt();//背包能承受的重量
        LinkedList<beiBao> linkedList=new LinkedList();
        for (int i = 0; i <n ; i++) {
    
    
            beiBao b=new beiBao();
            b.values=scanner.nextInt();
            b.weight=scanner.nextInt();
            b.wValues=b.values/b.weight;
            linkedList.add(b);
        }
      Collections.sort(linkedList, new Comparator<beiBao>() {
    
    
          @Override
          public int compare(beiBao o1, beiBao o2) {
    
    
              return (int)(o2.wValues-o1.wValues);
          }
      });
        for (int i = 0; i <n ; i++) {
    
    
            System.out.println(linkedList.get(i).wValues);
        }
        double allvalues=0;
        boolean[] flag=new boolean[n];
        for (int i = 0; i < flag.length; i++) {
    
    
            flag[i]=false;//表示没有放入背包中
        }
        for (int i = 0; i <n ; i++) {
    
    
            if(linkedList.get(i).weight<=weight){
    
    
                flag[i]=true;
                weight=weight-linkedList.get(i).weight;
                allvalues+=linkedList.get(i).values;
                System.out.println("重量为:"+linkedList.get(i).weight+"价格为:"+linkedList.get(i).values+"可以完全装入");
            }
        }
        for (int i = 0; i <n ; i++) {
    
    
            if(!flag[i]){
    
    
                double rate=(double)weight/linkedList.get(i).weight;
                allvalues=allvalues+rate*linkedList.get(i).values;
                weight-=linkedList.get(i).weight;
                System.out.println("重量为:"+linkedList.get(i).weight+"价值为:"+linkedList.get(i).values+"装入比例为:"+rate);
            }
        }
        System.out.println("总价值为:"+allvalues);
    }
}

problema de travessia de barco

A descrição comum desse problema é: há n pessoas que precisam atravessar o rio, e há apenas um barco, que comporta no máximo duas pessoas.A velocidade da corda é a velocidade da mais lenta das duas pessoas. Quanto tempo levará pelo menos para transportar n pessoas para o outro lado.

Supondo que o tempo gasto por essas pessoas seja armazenado em um array, organizado em ordem crescente, ou seja, de rápido para lento, o tempo gasto por cada pessoa é tempo[0], tempo[1]...tempo[n]

Quando o número de pessoas > = 4, há duas opções para este problema:
1. O mais rápido e o segundo mais rápido cruzam o rio primeiro, e depois o mais rápido rema o barco de volta; o segundo mais lento e o mais lento atravessam o rio e depois o segunda volta mais rápida, neste ponto calculamos o tempo gasto no processo seguinte

ícone

A segunda forma:

1. O mais rápido e o mais lento a cruzar o rio, o mais rápido a remar o barco de volta, o mais rápido e o segundo mais lento a cruzar o rio e o mais rápido a voltar

ícone
[Falha na transferência da imagem do link externo, o site de origem pode ter um mecanismo anti-leeching, é recomendável salvar a imagem e carregá-la diretamente (img-W2UHDjrr-1678500194755) (C:/Users/Hongyan/AppData/Roaming/Typora/ typora-user-images/ imagem-20230311092120755.png)]

Existem duas maneiras de atravessar o rio acima, ao usá-lo, você pode comparar qual tem menos tempo antes de usá-lo.

Se o número de pessoas for inferior a 4

1. Quando o número de pessoas é três, o horário fixo neste horário é hora[0]+hora[1]+hora[2]

2. Quando o número de pessoas é dois, o horário fixo neste horário é o horário[1]

3. Quando o número de pessoas é um, o tempo fixo é time[0]

/*
 * 有n个人需要过河,只有一艘船,最多能乘2人,船的运行速度为2人中较慢一人的速度,
 * 过去后还需一个人把船划回来,把n个人运到对岸,最少需要多久。
 */
public class River {
    
    
    public static void main(String[] args) {
    
    
    	int[] times={
    
    1,2,4,5,8};
    	int result=crossRiver(times);
    	System.out.println("所花时间为:"+result);
    }
    
    private static int crossRiver(int[] times){
    
    
    	/*n表示还未过河的人数,初始化为所有人*/
    	int n=times.length;
    	int result=0;
    	while(n>0){
    
    
    		if(n==1){
    
    
    			result=result+times[0];
    			break;
    		}else if(n==2){
    
    
    			result=result+times[0]+times[1];
    			break;
    		}else if(n==3){
    
    
    			result=result+times[0]+times[1]+times[2];
    			break;
    		}else{
    
    
    			/*在每次过河时,在两种方式上进行比较,选择耗时更少的那个*/
    			result=result+Math.min(times[1]+times[0]+times[n-1]+times[1],times[n-1]+times[0]+times[n-2]+times[0]);
    			/*无论采取哪种方式,最后的结果都是讲最慢的和次慢的运送过河,也就是数组的最后两位,所以此处可简单地将数组长度-2*/
    			n=n-2;
    		}
    	}
    	return result;
    }
}

cobertura de área

Este problema descreve: dado um intervalo com comprimento m, e dado o ponto inicial e o ponto final (intervalo fechado) de n segmentos de linha, encontre o menor número de segmentos de linha que pode cobrir completamente todo o intervalo. As etapas são as seguintes :

  1. Dentre todos os intervalos a serem selecionados, são eliminados os intervalos cujo ponto inicial e ponto final estão fora da faixa necessária.
  2. Classificar todos os intervalos por origem
  3. O primeiro ponto é selecionado por padrão e, no processo de seleção de pontos, os seguintes princípios devem ser seguidos: o ponto inicial do novo intervalo deve ser menor que o ponto final do intervalo atual e o ponto final do intervalo novo intervalo deve ser maior que o ponto inicial do intervalo atual
  4. Repita a etapa 3 em um loop até que o valor do ponto final do intervalo atual >= o valor esperado do ponto final e finalize o processo de localização do intervalo
import java.util.*;
class quJian{
    
    
    int pre;
    int end;//表示区间的起点和终点
}
public class 区间覆盖 {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner=new Scanner(System.in);
        //加入区间 10个
        int n=scanner.nextInt();
        int m=scanner.nextInt();
        //假设所求区间范围为[n,m];
        LinkedList <quJian>linkedList=new LinkedList();
        for (int i = 0; i <7 ; i++) {
    
    
            quJian qj=new quJian();
            qj.pre=scanner.nextInt();
            qj.end=scanner.nextInt(); //1.删除起点和终点在所要求范围外的区间
            if(!(qj.pre<n&&qj.end<n||qj.pre>m&&qj.end>m)){
    
    
                linkedList.add(qj);
            }
        }
        //先进行排序
        Collections.sort(linkedList, new Comparator<quJian>() {
    
    
            @Override
            public int compare(quJian o1, quJian o2) {
    
    
                return o1.pre-o2.pre;//按照小区间的起点排下序,升序
            }
        });
       //然后进行选取
        boolean flag=false;
        int count=1;//计数
        System.out.println("起点:"+linkedList.get(0).pre+","+linkedList.get(0).end);
        int end=linkedList.get(0).end;
        for (int i = 1; i <linkedList.size() ; i++) {
    
    
            if(linkedList.get(i).pre<=end&&linkedList.get(i).end>end){
    
    
                end=linkedList.get(i).end;//更新end点
                for (int j = i+1; j <linkedList.size() ; j++) {
    
    
                    //然后从这后面找到最长的end
                    if(linkedList.get(j).end>end){
    
    
                        end=linkedList.get(j).end;
                        count++;//找到一个区间
                        System.out.println(linkedList.get(j).pre+" "+linkedList.get(j).end);
                        if(end>=m){
    
    
                            flag=true;
                        }
                    }

                }
            }
            if(flag){
    
    
                break;
            }
        }
        System.out.println(count);
    }
}

Acho que você gosta

Origin blog.csdn.net/qq_63319459/article/details/129459640
Recomendado
Clasificación