Diseño y análisis de algoritmos: Capítulo 3 Recursión y estrategia de división y conquista

La idea de diseño del método de divide y vencerás es dividir un gran problema que es difícil de resolver directamente en los mismos problemas de menor escala, de modo que cada uno pueda desglosarse, dividirse y regirse. Si el problema original se puede dividir en k subproblemas (1 <k≤n), y estos subproblemas se pueden resolver, y las soluciones de estos subproblemas se pueden utilizar para encontrar la solución del problema original, entonces este método de dividir y conquistar es factible. El subproblema generado por el método de divide y vencerás es a menudo un modelo más pequeño del problema original, que proporciona conveniencia para el uso de técnicas recursivas.
3.1 Algoritmo recursivo La
llamada directa o indirecta del programa a sus habilidades de programación se denomina algoritmo recursivo (Recursion).
La recursión debe tener condiciones de contorno, segmento de avance recursivo y segmento de retorno recursivo.

  • Cuando no se cumplan las condiciones de contorno, avance recursivamente;
  • Cuando se cumplen las condiciones de contorno, retorno recursivo.
  • Nota: Cuando se usa la estrategia de recursión incremental, debe haber una condición clara de fin de recursión, llamada salida de recursión, de lo contrario continuará indefinidamente (punto muerto).

Desventajas de la recursividad:

  • La eficiencia del algoritmo recursivo para resolver problemas es baja.
  • En el proceso de llamada recursiva, el sistema abre una pila para almacenar el punto de retorno y las variables locales de cada capa. Demasiados tiempos de recursión pueden causar fácilmente el desbordamiento de la pila.

3.1.1 Algoritmo de secuencia de Fibonacci
Inserte la descripción de la imagen aquí
3.1 Algoritmo recursivo de secuencia de Fibonacci

int fib(int n) 
{  
	if (n<=1) 
		return 1;  
	return fib(n-1)+fib(n-2); 
}

La eficiencia de este algoritmo es muy baja porque la recursividad se repite demasiadas veces.
Algoritmo 3.2 Algoritmo recursivo de la secuencia de Fibonacci

int  fib[50];
void fibonacci(int n) 
{  
	fib[0] = 1;   
	fib[1] = 1;
	for (int i=2; i<=n; i++)   
		fib[i] = fib[i-1]+fib[i-2]; 
}

3.1.2 Algoritmos para el problema de permutación completo de
Inserte la descripción de la imagen aquí
conjuntos 3.3 Recurrencia del problema de permutación

void Perm(int list[], int k, int m) 
{
	if(k==m)  
	{   
		for(int i=0;i<=m;i++)    
			cout<<list[i]<<" ";   
		cout<<endl;
	}  
	else
		for(int j=k;j<=m;j++)   
		{
			swap(list[k],list[j]);    
			Perm(list,k+1,m);    
			swap(list[k],list[j]);   
		} 
}

3.1.3 Problema de partición
entera El problema de partición entera es una de las proposiciones clásicas del algoritmo. Representa un entero positivo n como la suma de una serie de enteros positivos: n = n1 + n2 +… + nk (donde n1> = n2> =…> = nk> = 1, k> = 1)
Esta representación del entero positivo n Esto se llama la división del número entero positivo n. El número de divisiones diferentes de un entero positivo n se llama número de división de un entero positivo n, y se denota como p (n).
El entero positivo 6 tiene las siguientes 11 divisiones diferentes, por lo que p (6) = 11.
6
5 + 1
4 + 2, 4 + 1 + 1
3 + 3, 3 + 2 + 1, 3 + 1 + 1 + 1
2 + 2 + 2, 2 + 2 + 1 + 1, 2 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1 + 1 + 1
algoritmo de análisis de
Inserte la descripción de la imagen aquí
algoritmo 3.4 algoritmo de partición de entero positivo n

int split(int n,int m) 
{  
	if(n==1||m==1) 
		return 1;  
	else if (n<m) 
		return split(n,n);
	else if(n==m) 
		return split(n,n-1)+1;  
	else 
		return split(n,m-1)+split(n-m,m); 
}

3.2 Estrategia de
dividir y conquistar La estrategia de dividir y conquistar es para un problema de tamaño n. Si el problema se puede resolver fácilmente (por ejemplo, si el tamaño de n es pequeño), se resuelve directamente, de lo contrario se descompone en k subproblemas de tamaño pequeño Los subproblemas son independientes entre sí y tienen la misma forma que el problema original.
Resuelva estos subproblemas de forma recursiva y luego combine las soluciones de cada subproblema para obtener el problema original.
3.2.1 Pasos básicos del
método de divide y vencerás El método de dividir y vencerás tiene tres pasos en cada nivel de recursión:

  1. Descomposición : descomponga el problema original en varios subproblemas de menor escala, independientes entre sí y en la misma forma que el problema original;
  2. Solución : si el subproblema es pequeño y fácil de resolver, resuélvalo directamente; de ​​lo contrario, resuelva cada subproblema de forma recursiva;
  3. Fusionar : fusionar las soluciones de cada subproblema en el problema original.

Algoritmo 3.5 Patrón de diseño de algoritmo de estrategia de divide y vencerás

Divide_and_Conquer(P) 
{  
	if (|P|<=n0 ) 
		return adhoc(P);  
	divide P into smaller substances P1,P2,…,Pk;
	for (i=1; i<=k; k++)    
		yi=Divide-and-Conquer(Pi)  
	Return merge(y1,y2,…,yk)      
}

Al diseñar algoritmos utilizando el método de dividir y conquistar, es mejor hacer que la escala de los subproblemas sea aproximadamente la misma. Si se divide en k subproblemas de igual tamaño, muchos problemas pueden tomar k = 2. Este enfoque de hacer que los subproblemas sean aproximadamente iguales se deriva de la idea de un subproblema de equilibrio (equilibrio), que casi siempre es mejor que la práctica de subproblemas desiguales.
3.2.2 Condiciones aplicables del
método de dividir y conquistar Los problemas que puede resolver el método de dividir y conquistar generalmente tienen las siguientes características:

  1. Este problema se puede resolver fácilmente reduciendo la escala hasta cierto punto;
  2. El problema puede descomponerse en varios problemas idénticos de menor escala, es decir, el problema tiene la propiedad óptima de la subestructura;
  3. Las soluciones de los subproblemas descompuestos por este problema pueden combinarse en la solución de este problema;
  4. Los subproblemas descompuestos por este problema son independientes entre sí, es decir, no hay subproblemas comunes entre los subproblemas.

3.2.4 Horario de round-robin
Descripción del problema: Hay n = 2k atletas para jugar un torneo de round-robin.
Ahora necesitamos diseñar un horario de juego que cumpla con los siguientes requisitos:

  1. Cada jugador debe competir con otros jugadores n-1 una vez;
  2. Cada jugador solo puede competir una vez al día;
  3. El juego round robin termina en n-1 días.

Diseñe el calendario del juego como una tabla con n filas y n-1 columnas de acuerdo con este requisito. En la i-ésima fila y la j-ésima columna de la tabla, complete el jugador que el i-ésimo jugador se reunió el día j, donde 1≤i≤n, 1≤j≤n-1.

void Table(int k) 
{   
	int i, r;  
	int n = 1 << k; 
	for (i=0; i<n; i++)   
		a[0][i] = i + 1;
	for (r=1; r<n; r<<=1)   
		for (i=0; i<n; i+=2*r)   
		{     
			Copy(r, r + i, 0, i, r);  
			Copy(r, i, 0, r + i, r);
		} 
}

Realizar la copia de matriz cuadrada

//源方阵的左上角顶点坐标(fromx, fromy),行列数为r 
//目标方阵的左上角顶点坐标(tox, toy),行列数为r 
void Copy(int tox, int toy, int fromx, int fromy, int r) 
{
	for (int i=0; i<r; i++)   
		for (int j=0; j<r; j++)      
			a[tox+i][toy+j] = a[fromx+i][fromy+j]; 
}

3.2.6 Problema de selección
Para una matriz dada de n elementos a [0: n-1], es necesario encontrar el k-ésimo elemento más pequeño.
Entrada Hay
múltiples conjuntos de casos de prueba.
Hay 2 líneas para cada caso de prueba, la primera línea es enteros n y k (1≤k <n≤1000), y la segunda línea es n enteros.
Salida
del k-ésimo elemento más pequeño.
Use la idea del algoritmo de ordenación rápida para resolver el problema de selección.
Después de recordar una ordenación rápida, el número de elementos en el subconjunto izquierdo se descompone para dejarlo a la izquierda, entonces el problema de selección puede ser una de las siguientes situaciones:

  1. nleft = k ﹣ 1, entonces los datos del límite son la respuesta a la pregunta de selección.
  2. nleft> k ﹣ 1, luego la respuesta a la pregunta seleccionada se sigue encontrando en el subconjunto izquierdo, y el tamaño de la pregunta se hace más pequeño.
  3. nleft <k ﹣ 1, luego la respuesta a la pregunta de selección se sigue encontrando en el subconjunto correcto, la pregunta se convierte en seleccionar el número más pequeño de k-nleft-1 y la escala de la pregunta se vuelve más pequeña.

Algoritmo 3.9 Algoritmo para encontrar el k-ésimo elemento más pequeño utilizando una estrategia de divide y vencerás

int select(int left,int right,int k)
{
	if(left>=right)
		return a[left];
	int i=left;
	int j=right+1;
	int pivot=a[left];
	while(true)
	{
		do{
			i=i+1;
		}while(a[i]<pivot);
		do{
			j=j-1;
		}while(a[j]>pivot);
		if(i>=j)
			break;
		swap(a[i],a[j]);
	}
		if(j-left+1==k)
			return pivot;
		a[left]=a[j];
		a[j]=pivot;
		if(j-left+1<k)
			return select(j+1,right,k-j+left-1);
		else
			return select(left,j-1,k);
}

3.2.7 Problema del oleoducto
Una compañía petrolera planea construir un oleoducto principal de este a oeste. La tubería pasará a través de un campo petrolero con n pozos petroleros. Desde cada pozo, se debe conectar un oleoducto a la tubería principal a lo largo de la ruta más corta (o sur o norte).
Si se dan las posiciones de n pozos petroleros, es decir, sus coordenadas x (este-oeste) e coordenadas y (norte-sur), cómo determinar la posición óptima de la tubería principal, incluso si la suma de la longitud de la tubería de petróleo de cada pozo a la tubería principal es la más pequeña Ubicación?
Dadas las posiciones de n pozos petroleros, programe y calcule la suma de la longitud mínima del oleoducto desde cada pozo hasta el oleoducto principal.
La
línea de entrada 1 es un número entero n que representa el número de pozos de petróleo (1≤n≤10 000). Las siguientes n filas son las posiciones de los pozos de petróleo, con dos enteros x e y por fila (- 10 000≤x, y≤10 000).
Salida
La suma de la longitud mínima del oleoducto desde cada pozo hasta el oleoducto principal.
Suponga que las posiciones de n pozos petroleros son pi = (xi, yi), i = 1 ~ n. Dado que el oleoducto principal es este-oeste , la coordenada y de su eje principal se puede usar para determinar su posición de manera única. La posición óptima y de la tubería principal debe encontrarse:
Inserte la descripción de la imagen aquí
desde el teorema de la mediana, y es la mediana.
Algoritmo 1: Ordene la matriz a (generalmente en orden ascendente), tome el elemento del medio

int n;
int x;
int a[1000];
cin>>n; 
for(int k=0;k<n;k++)  
	cin>>x>>a[k];
sort(a,a+n);
int min=0; 
for(int i=0;i<n;i++)  
	min += (int)fabs(a[i]-a[n/2]); 
cout<<min<<endl;

Algoritmo 2: usa la estrategia de dividir y conquistar para encontrar la mediana

int n; 
int x; 
int a[1000]; 
cin>>n; 
for (int i=0; i<n; i++)  
	cin>>x>>a[i];
int y = select(0, n-1, n/2); 
int min=0; 
for(int i=0;i<n;i++)  
	min += (int)fabs(a[i]-y); 
cout<<min<<endl;

3.2.8 Problema de conjunto de medio número
Dado un número natural n, a partir de n, los números en el conjunto de conjunto de medio número (n) se pueden generar a su vez de la siguiente manera.
(1) conjunto n (n);
(2) Agregue un número natural a la izquierda de n, pero el número natural no puede exceder la mitad del número agregado recientemente;
(3) Procese de acuerdo con esta regla hasta que no se pueda agregar más número natural.
Por ejemplo, set (6) = {6, 16, 26, 126, 36, 136}.
Hay 6 elementos en el conjunto (6).
Tenga en cuenta que el medio conjunto es un conjunto múltiple .
Para un número natural dado n, programe para calcular el número de elementos en el conjunto (n) del medio conjunto.
Si el número de elementos en el conjunto (n) es f (n), entonces obviamente hay:
Inserte la descripción de la imagen aquí
Algoritmo 3.12 Algoritmo recursivo para calcular el problema del conjunto de medio número

int comp(int n) 
{  
	int ans=1;  
	if (n>1) 
		for(int i=1;i<=n/2;i++)   
			ans+=comp(i);  
	return ans; 
}

Algoritmo 3.13 Algoritmo recursivo para calcular problemas de conjunto de números medios: búsqueda de memoria

int a[1001]; 
int comp(int n) 
{  
	int ans=1;  
	if(a[n]>0)
		return a[n]; 
	for(int i=1;i<=n/2;i++)   
		ans+=comp(i);  
	a[n]=ans;
	return ans; 
}
48 artículos originales publicados · Me gusta 25 · Visita 2453

Supongo que te gusta

Origin blog.csdn.net/qq_43628959/article/details/105106625
Recomendado
Clasificación