Recursão, divisão e conquista e dicotomia

Um, recursão

1. Introdução

Chamamos essa função em uma função personalizada, que é recursiva.
É dividido em dois tipos:

  1. A função a chama a função a (recursão direta)
  2. A função a chama a função b, a função b chama a função a (recursão indireta)

Obviamente, a função será chamada continuamente quando for chamada, então a função recursiva consiste basicamente em duas partes

  1. Parte de saída de função
  2. Parte da função de chamada

Dê um exemplo simples:
encontre o fatorial de n

n Resultado (f (n)) Resultado (f (n))
n = 5 (função de chamada) f (5) = 5 * f (4) 5 * 24 = 120
n = 4 (função de chamada) f (4) = 4 * f (3) 4 * 6 = 24
n = 3 (função de chamada) f (3) = 3 * f (2) 3 * 2 = 6
Quando n = 2 (função de chamada) f (2) = 2 * f (1) 2 * 1 = 2
Quando n = 1 (saída de função) f (1) = 1 1

ps: Ao chamar uma função recursiva, o computador irá chamar o espaço da pilha, portanto, tome cuidado com o número de camadas recursivas! Para evitar MLE
ps: Ao chamar uma função, a função irá primeiro para a camada inferior e encontrará o resultado final, portanto, tome cuidado com o número de recursões! Para evitar TLE

A função é a seguinte:

int func(int n) {
    
    
//递归出口 
	if(n<=1) return 1;//0!=1
//调用函数 
	return func(n-1)*n;
}

2. Exemplos

①, encontre o maior divisor comum

Limite de memória: 128 MiB
Limite de tempo: 1000 ms
Descrição do título:
Use um algoritmo recursivo para encontrar o maior divisor comum de dois números me n. (M> n> 0)
Amostra de entrada:
8 6
Amostra de saída:
mcd = 2
Análise:
Podemos usar a recursão para obter o método de lançamento e divisão

  1. Parte de saída de função: retorna n quando pode ser divisível
  2. Parte da função de chamada: retorna o maior fator comum do divisor e o resto. A
    função é a seguinte:
int gcd(int m,int n) {
    
    
	if(! m % n) return n;
	return gcd(n, n % m);
}

②, arranjo completo

Limite de memória: 128 MiB
Limite de tempo: 1000 ms
Descrição do título:
Dada uma string composta de diferentes letras minúsculas (comprimento <10), produza todas as permutações dessa string. Assumimos que há 'a' <'b' <... <'y' <'z' para letras minúsculas, e as letras na string dada foram organizadas em ordem crescente.
Entrada de amostra:
abc
Exemplo de saída:
abc
acb
bac
bca
táxi
CBA
Análise:
Podemos usar as primeiras letras como parâmetros, e usar isto como uma exportação recursiva.
Outras vezes, procuramos caracteres não utilizados em toda a string . E armazene-o no array a ser gerado.

Código principal:

int n;//记录字符串长
bool a[10];//记录字符状态
char s[30],//记录输入的字符串
	ans[10];//记录一种可行解
void f(int m) {
    
    
	if(m==n) {
    
    //出口
		cout<<ans<<'\n';//输出答案
		return;//结束函数
	}
	for(int i=0;i<n;i++) {
    
    //从整个字符串中寻找
		if(a[i]==false) {
    
    //用数组记录状态
			ans[m]=s[i];//记录答案
			a[i]=true;//标记为使用过
			f(m+1);//长度+1,继续枚举答案
			a[i]=false;//(找到答案后)重新找下一种答案,而该字符则标记为未使用
		}
	}
}
int main() {
    
    
	cin>>s;//输入
	n=strlen(s);//计算长度
	f(0);//递归
	return 0;
}

Em segundo lugar, divida e conquiste

1. Introdução

De um modo geral, dividir e conquistar é decompor um problema original em vários subproblemas, então resolver os subproblemas um por um e, finalmente, mesclar as soluções dos subproblemas nas soluções do problema original.
Dividir e conquistar
Pegue uma castanha:
entre em uma sequência e encontre os valores máximo e mínimo.
Não pense muito, vamos pensar primeiro: Qual é o subproblema? Como ele resolve isso?
Obviamente, quando o comprimento da sequência é um, o valor máximo pode ser conhecido imediatamente.
Em geral, você só precisa saber o valor máximo de um número de subsequências para saber o valor máximo da sequência.
código mostrado abaixo:

int fmax(int l,int r) {
    
    
	if(l==r) return a[l];//序列长度为一,返回序列中的唯一元素
	return max(fmax(l,(l+r)/2),fmax((l+r)/2+1,r));//左边的最小值 和 右边的最大值 的 最大值
}
int fmin(int l,int r) {
    
    
	if(l==r) return a[l];//序列长度为一,返回序列中的唯一元素
	return min(fmin(l,(l+r)/2),fmin((l+r)/2+1,r));//左边的最小值 和 右边的最小值 的 最小值
}

2. Exemplos de perguntas (poder rápido)

Limite de memória: 256 MiB
Limite de tempo: 1000 ms
Descrição do título:
Calcule o valor de a b
Amostra de entrada:
2 10
Amostra de saída:
1024
Análise: O
método recursivo da função é o seguinte:

Created with Raphaël 2.2.0 f(a,b) b==1 return a b%2==1 return a^(b/2)^2*a return a^(b/2)^2 yes no yes no

Três ou dois pontos

1. Introdução

Dividir significa literalmente o método de divisão em dois. Ou seja, um problema é dividido em dois, e a solução do problema é resolvida em um subproblema apropriado (intervalo).
Por exemplo:
em uma sequência não decrescente, encontre um determinado valor.
Formato de entrada: a
primeira linha contém um inteiro n, que é um comprimento de sequência não decrescente. 1 <= n <= 100.000.
A segunda linha contém n inteiros, são elementos de sequência não decrescentes . O tamanho de todos os elementos está entre 0-1.000.000.000.
A terceira linha contém um inteiro m, que é um determinado valor. O tamanho do valor fornecido está entre 0-1.000.000.000.
Formato de saída:
um caractere, 'Y' indica que há um determinado valor na sequência e 'N' significa que o oposto é a
dicotomia:
primeiro, se houver apenas um elemento restante na sequência (l == r), retorna se o elemento é igual ao valor dado (a [l] == t).
Então, dicotomia do meio.
Como a sequência é ordenada , dicotomizamos a sequência. Se o valor fornecido for maior que o número no meio da sequência (a [(l + r) / 2]> t), vá para o subintervalo esquerdo (l, (l + r) / 2-1) , caso contrário, procure o intervalo correto ((l + r) / 2 + 1, r) .
tal como:

1 2 3 5 7

Encontre o número 2 O
intervalo da primeira pesquisa é 1 ~ 5;
o intervalo da segunda pesquisa é 1 ~ 2;
o intervalo da terceira pesquisa é 2 ~ 2 (encontrado)

2. Exemplos

Segmentação da sequência numérica
Limite de memória: 512 MiB
Limite de tempo: 1000 ms
Descrição do título:
Para um determinado comprimento n sequência inteira positiva a, ela agora deve ser dividida em m segmentos, e cada segmento deve ser contínuo e a soma máxima de cada segmento O valor é o menor.
Amostra de entrada:
5 3
4 2 4 5 1
Amostra de saída:
6
Análise:
obtenha a pergunta A
primeira coisa: encontre le r. Ou seja, o escopo do problema.
No mínimo, pode ser o valor máximo dos elementos na sequência; no
máximo, pode ser a soma dos elementos na sequência.

cin>>n>>m;
for(int i=1;i<=n;i++) {
    
    
    cin>>a[i];
    l=max(l,a[i]),r+=a[i];
}

A segunda coisa: encontre a condição dicotômica.

bool f(int x) {
    
    
    int sum=1,t=0;//在传入的限定的最大值中累计需要的区间数量
    for(int i=1;i<=n;i++)
        if(t+a[i]<=x) t+=a[i];
        else sum++,t=a[i];
    return sum<=m;//传回是否符合要求
}

A terceira coisa: encontre uma dicotomia.
Valor máximo dicotômico.

while(l<=r) {
    
    
	int mid=(r+l)/2;
	if (f(mid))
		ans=mid,r=mid-1;
	else l=mid+1;
}

Quatro, resumo

Recursão, divisão e conquista e dicotomia usam funções para chamar a si mesmos para encontrar a resposta. Ela pode retornar o valor que você precisa por meio do método de execução que você projetou, e a estrutura de loop escrita com ela será mais concisa .
Espero que você aprenda mais sobre as funções após a aprovação deste artigo.

Acho que você gosta

Origin blog.csdn.net/weixin_49692699/article/details/114878004
Recomendado
Clasificación