Énumération récursive


Dans la plupart des cas, les problèmes d'énumération sont résolus par une énumération récursive. Malheureusement, il n'y a pas d'approche générale, car souvent, les méthodes d'itération dépendent fortement de la tâche elle-même.
Cependant, on peut remarquer une certaine similitude entre les méthodes d'énumération des divers objets combinatoires. Par conséquent, pour un exemple illustratif, considérons un code qui génère toutes les combinaisons de n à k.
  entier n, k ; // Un tableau qui prend en charge les préfixes de combinaison. // // Le préfixe dans ce cas signifie que nous avons sélectionné des objets dans la combinaison. // // Par la suite, ils seront soit complétés dans la bonne combinaison, // ou la récursivité coupera la branche lorsqu'elle se rendra compte qu'un tel préfixe ne peut pas être complété correctement vecteur arr ; // la fonction d'itération récursive elle-même // // pos - numéro de l'objet en combinaison, que nous déterminerons au moment actuel (de 0 à k - 1) // // prev - la valeur de l'objet qui a été prise à l'étape précédente // // Dans les combinaisons, l'ordre des objets n'est pas important ([1, 2] et [2, 1] sont considérés comme identiques et similaires), // donc nous voulons uniquement générer des ensembles où les valeurs d'objets sont dans l'ordre croissant. // // Ceci est nécessaire pour que les mêmes options telles que [1, 2] et [2, 1] ne soient itérées qu'une seule fois // (dans notre cas, nous ne considérerons que [1, 2], mais pas [2, 1] car ce n'est pas un ensemble ordonné). // // C'est pourquoi on garde la variable prev pour réduire le nombre d'itérations void rec(int pos, int prev) { // si le numéro de l'élément sélectionné est égal à k, alors nous avons déjà sélectionné tous les éléments // parce que leurs nombres vont de 0 à k - 1 si (pos == k) { // si la condition est remplie, alors la bonne combinaison est maintenant dans le tableau arr // et nous pouvons le traiter d'une manière ou d'une autre (dans ce cas, imprimez-le simplement sur la console) pour (int i = 0; je < k; i++) cout << arr[i] + 1 << ' '; cout << '\n'; retour; // coupe la branche de récursivité, car déjà obtenu une combinaison et pas besoin de continuer plus loin } // Ici, nous vérifions si nous pouvons obtenir la bonne combinaison à partir des éléments restants // s'il n'y a pas assez d'éléments restants, alors on coupe cette branche de récursivité si (k - pos > n - préc - 1) retour; // itération sur l'objet que nous avons mis sur la position avec l'index pos // mais parce que nous voulons un ensemble ordonné, alors la valeur minimale possible est prev + 1 for (int i = prev + 1; i < n; i++) { arr.push_back(i); // Ajoute un élément au tableau global. Maintenant, nous semblons l'avoir choisi rec(pos + 1, je); // exécution récursive pour définir les éléments suivants arr.pop_back(); // après retour de la récursivité, notre élément actuellement sélectionné n'est plus pertinent, // parce que à l'intérieur de la récursivité, nous avons parcouru toutes les options avec un tel préfixe, // donc cet élément doit être supprimé } } int main() { cin>> n>> k; // initialement nous placerons l'élément à la 0ème position // nous supposons que l'élément -1 a été sélectionné auparavant, de sorte que l'itération commence initialement à partir d'un objet nul rec(0, -1); renvoie 0 ; }

Le même code, mais sans commentaires :
  entier n, k ; vecteur arr ; void rec(int pos, int prev) { si (pos == k) { pour (int i = 0; je < k; i++) cout << arr[i] + 1 << ' '; cout << '\n'; retour; } si (k - pos > n - préc - 1) retour; for (int i = prev + 1; i < n; i++) { arr.push_back(i); rec(pos + 1, je); arr.pop_back(); } } int main() { cin>> n>> k; rec(0, -1); renvoie 0 ; }  
Dans les itérations récursives, une attention particulière doit toujours être portée aux coupes récursives. En pratique, ils peuvent considérablement accélérer le programme.