<<O algoritmo é lindo>>——(5)—— Estrutura central do algoritmo de retrocesso (1)

contente

prefácio

conceito básico

estrutura básica 

Exemplo: matriz completa


prefácio

Este blog é do framework de rotina de resolução de problemas do algoritmo de retrocesso:: folha de dicas do algoritmo do labuladong (gitee.io) , quero fazer uma nota de estudo aqui e aprender com todos para aprofundar a impressão.

Versão do vídeo: [labuladong] Explicação detalhada da rotina central do algoritmo de retrocesso _ beep mile _bilibili

conceito básico

O algoritmo de retrocesso é na verdade um processo de tentativa de busca semelhante à enumeração. É principalmente para encontrar a solução do problema durante o processo de tentativa de busca. caminho. O método de backtracking é um método de busca ótima , que busca para frente de acordo com as condições ótimas para atingir o objetivo. No entanto, quando a exploração atinge um determinado passo, e constata-se que a escolha original não é a ideal ou não atinge o objetivo, ela dará um passo atrás e uma nova seleção, os chamados "pontos de retrocesso". Muitos problemas complexos e de grande escala podem usar o método de retrocesso, conhecido como "método universal de solução de problemas" 

estrutura básica 

1. O algoritmo de retrocesso é um tipo de método de exaustão de força bruta

2. O processo exaustivo é, na verdade, o processo de atravessar uma árvore multi-fork .

3. A estrutura de código do algoritmo de retrocesso é semelhante à estrutura de código de travessia de várias árvores

Estando em um certo nó do retrocesso, precisamos pensar em três questões:

  • Caminho: a escolha que fazemos atualmente
  • Lista de escolhas: as escolhas que podemos fazer no momento
  • Condição final : ou seja, ao chegar ao final da árvore de decisão, não é possível fazer mais escolhas

quadro, Armação:

result=[]; 

def backtrack(路径,选择列表)

     if 满足结束条件: 

        result.add(路径);

        return 

    for 选择 in 选择列表 

        做选择

        backtrack(路径,选择列表)

        撤销选择
 

A parte central do código acima é fazer uma seleção no loop for, depois recuar e desfazer a seleção. Talvez eu ainda esteja confuso sobre as três perguntas acima, não entendo muito bem, não importa, não entre em pânico, apenas mencione aqui, apenas deixe algumas impressões. Em seguida, vou fazer dois tópicos clássicos de permutação completa e N rainha com você, e analisá-los em profundidade.

Exemplo: matriz completa

Como pensamos sobre a cápsula quando recebemos essa pergunta? Primeiro, vamos dar um exemplo de {1,2,3}. Normalmente fixamos um número 1, então o segundo dígito pode levar 2, o último dígito pode levar 3 ou o segundo dígito pode levar 3 e o último dígito pode pegue 2. E então enumerar exaustivamente os dois últimos...

Isso parece confuso, então vamos desenhar uma árvore de retrocesso para nos ajudar a entender

 De acordo com essa árvore de retrocesso, ela é percorrida a partir da raiz, que na verdade são todas as permutações. Podemos também chamar essa árvore de retrocesso de árvore de decisão. A razão pela qual dizemos isso é que ficamos em cada nó para decidir para onde ir. Olhando para a figura, se estivermos em 1.

 Podemos escolher 2 ou 3 agora, porque não podemos escolher 1 cápsula, porque já andamos antes, e o arranjo completo não permite repetição.

1 é o caminho, registrando o caminho que percorremos . 2 e 3 são a lista de seleção , indicando o caminho que podemos escolher atualmente . A condição final é quando o nó folha inferior da árvore transversal está vazio e a lista de seleção está vazia .

 A função que definimos é  backtrack na verdade como um ponteiro. Ele caminha nessa árvore e mantém os atributos de cada nó corretamente. Sempre que vamos ao fundo da árvore, seu "caminho" é um arranjo completo.

Na verdade, é um problema de travessia de árvore, e a estrutura de travessia da árvore multi-fork é assim:

void traverse(TreeNode root) {
    for (TreeNode child : root.childern) {
        // 前序遍历需要的操作
        traverse(child);
        // 后序遍历需要的操作
    }
}

 A chamada travessia de pré-ordem e travessia de pós-ordem são apenas coisas feitas em dois pontos de tempo diferentes. entrando em um certo nó.

 Lembrando o que acabamos de dizer, "caminho" e "seleção" são atributos de cada nó. Para manter adequadamente os atributos de um nó quando uma função percorre a árvore, ela precisa fazer algo nesses dois momentos especiais:

 Agora você entende a estrutura central do retrocesso?

for 选择 in 选择列表:
    # 做选择
    将该选择从选择列表移除
    路径.add(选择)
    backtrack(路径, 选择列表)
    # 撤销选择
    路径.remove(选择)
    将该选择再加入选择列表

Código: 

List<List<Integer>> res = new LinkedList<>();

/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
    // 记录「路径」
    LinkedList<Integer> track = new LinkedList<>();
    // 「路径」中的元素会被标记为 true,避免重复使用
    boolean[] used = new boolean[nums.length];
    
    backtrack(nums, track, used);
    return res;
}

// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素(used[i] 为 false)
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track, boolean[] used) {
    // 触发结束条件
    if (track.size() == nums.length) {
        res.add(new LinkedList(track));
        return;
    }
    
    for (int i = 0; i < nums.length; i++) {
        // 排除不合法的选择
        if (used[i]) {
            // nums[i] 已经在 track 中,跳过
            continue;
        }
        // 做选择
        track.add(nums[i]);
        used[i] = true;
        // 进入下一层决策树
        backtrack(nums, track, used);
        // 取消选择
        track.removeLast();
        used[i] = false;
    }
}

 O que vemos com mais frequência na escada da Blue Bridge Cup é o seguinte código

 


#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int ants ;
int a[10] = { 0,1,2,3,4,5,6,7,8,9 };

void f(int k)
{
	if (k == 10)
	{
       //这里一般还会有个判断条件
		return;
	}
//从k位开始的每个字符,都尝试放在K这个位置
	for (int i = k; i < 10; i++)
	{
		{int temp = a[i]; a[i] = a[k]; a[k] = temp; }//把后面的每个数字都换到k位
		f(k + 1);
		{int temp = a[i]; a[i] = a[k]; a[k] = temp; }//回溯
	}
}

int main()
{
	f(0);
	cout << ants << endl;
	return 0;
}

Amigos que estudaram C++ sabem que podem ser implementados diretamente com next_permutation

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int ants;
int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
int main()
{
	do {
			ants++;
	} while (next_permutation(a, a + 10));
	//f(0);
	cout << ants << endl;
	return 0;
}

Claro, esta questão também pode ser feita por soma de prefixo, iteração e outros métodos.Os parceiros interessados ​​podem descer e tentar. Até agora, a estrutura do nosso algoritmo de retrocesso foi introduzida.No próximo artigo, resolveremos as N rainhas.

 

Acho que você gosta

Origin blog.csdn.net/m0_58367586/article/details/123867947
Recomendado
Clasificación