Recursão, dicotomia e dividir e conquistar
Um, recursão
1. Introdução
Chamamos essa função em uma função personalizada, que é recursiva.
É dividido em dois tipos:
- A função a chama a função a (recursão direta)
- 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
- Parte de saída de função
- 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
- Parte de saída de função: retorna n quando pode ser divisível
- 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.
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:
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.