Enumeration about permutation and combination

Enumeration about permutation and combination

Recently, I encountered these two types of questions when I was studying the questions, mainly calculating the number of permutations and the number of combinations. After I did it, I saw some problem solving and found that it helped a lot.

1. Combined enumeration

Luogu P1157 combined output

Combination is to extract r elements from n elements (no order and r ≤ n). We can simply understand n elements as natural numbers 1, 2, …, n, and take any r number from them.

Take a chestnut: For example, n = 5, r = 3, then all the combinations are: 123, 124, 125, 145, 234, 235, 245, 345


Solution 1 : The most easy to think of this question is dfs, recursive solution, search and backtracking, relatively simple, directly on the code:

// 洛谷P1157 组合的输出
#include<iostream>
using namespace std;
int n, r, num[22];
void func(int index, int start) {
    
    
	if (index == r - 1) {
    
    		// 已经取满r个时输出
		for (; start <= n; start++) {
    
    
			for (int i = 0; i < r - 1; i++) {
    
    
				printf("%3d", num[i]);
				//cout << num[i] << " ";
			}
			printf("%3d\n", start);
			//cout << start << endl;
		}
	}
	else if(index < r - 1){
    
    		// 不满r个时继续递归
		while (start <= n - r + index + 1) {
    
    
			num[index] = start;
			++start;
			func(index + 1, start);		// 递归求解后回溯
		}
	}
}
int main()
{
    
    
	cin >> n >> r;
	func(0, 1);
	return 0;
}

Solution 2 : Although the above recursive solution is easy to think of, it has certain problems. When r is relatively large, the depth of recursion will be very deep, which may cause the program to fail to run, so non-recursive solution is very necessary

There are mainly four branches (take the input m = 5, r = 3 as an example):

  1. Filled with r: output the result directly, for example index = 4, num = [1, 2, 3]
  2. When it is not filled and the current bit is 0: add the current value to the previous bit, for example index = 3, num = [1, 2, 0] -> [1, 2, 3]
  3. When it is not filled and the current digit is not 0: judge whether the current digit can be increased by one (not exceeding the range of n, for example: index = 3, num = [1, 2, 3] -> [1, 2, 4 ]
  4. If the above conditions are not met, go back one bit and set the current position to 0, for example: index = 3, num = [1, 2, 5] -> index = 2, num = [1, 2, 0]
// 洛谷P1157 组合的输出
#include<iostream>
#include<string.h>
using namespace std;
int main() {
    
    
	int n, r, num[22], index = 1;
	memset(num, 0, 22 * sizeof(int));
	scanf("%d%d", &n, &r);
	while (index > 0) {
    
    
		if (index >= r + 1) {
    
    		// 当填满时,输出
			for (int i = 1; i <= r; i++) {
    
    
				printf("%3d", num[i]);
			}
			printf("\n");
			index--;		// 回溯到前一位
			continue;
		}
		if (num[index] == 0) {
    
    		// 当该位为0时,赋值为前一位加1
			num[index] = num[index - 1] + 1;
			index++;
			continue;
		}
		if (num[index] < n - r + index) {
    
    	// 核心步骤:判断当前位是否还能递增
			num[index]++;
			index++;
			continue;
		}
		num[index] = 0;		//  如果上述条件都不满足,则向前回溯
		index--;
	}
	return 0;
}

2. Sorted enumeration

Luogu P1706 full array problem

Luogu P1088 Martian

Output all non-repeating permutations of natural numbers 1 to n, that is, all permutations of n, and it is required that no repeated numbers appear in any number sequence generated.

Take a chestnut: For example, n = 3, then all the combinations are: 123, 132, 213, 231, 312, 321

Speaking of the previous, maybe C/C++ players will say that next_permutationit is enough, but this method is not considered here, and the focus is on developing ideas!


Solution 1 : The same idea, you can also use dfs deep search to enumerate all the situations, the idea is also very simple, the code is easy to understand:

// 洛谷P1706 全排列问题
#include<iostream>
#include<algorithm>
int num[10], tag[10] = {
    
     0 }, n;
using namespace std;
void dfs(int index) {
    
    
	if (index == n) {
    
    
		for (int i = 0; i < n; i++) {
    
    
			printf("%5d", num[i]);
		}
		printf("\n");
	}
	for (int i = 0; i < n; i++) {
    
    
		if (tag[i] == 0) {
    
    
			num[index] = i + 1;
			tag[i] = 1;
			dfs(index + 1);
			tag[i] = 0;
		}
	}
}
int main() {
    
    
	scanf("%d", &n);
	dfs(0);
	return 0;
}

Solution 2: Similarly, the above recursive solution also exists. If n is too large, the number of recursive layers will be too deep, which may cause the program to crash. For example, the Luogu P1088 Martian mentioned above , n even reaches the order of 10000, and then The above recursive solution is broken, so a non-recursive solution is needed.

The non-recursive solution is actually very natural. It simulates our thinking when calculating the next sorting number. I probably gave a little chestnut. You can refer to it if you need it:

0AeYRI.png

Then the code is implemented. In fact, just follow the above ideas strictly~

#include<iostream>
#include<algorithm>
int num[11000];
using namespace std;
int main() {
    
    
	ios::sync_with_stdio(false), cin.tie(NULL);
	int m, n, j, k;
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
    
    
		cin >> num[i];
	}
	for (int i = 0; i < m; i++) {
    
    
		j = n - 2;
		while (num[j + 1] < num[j]) {
    
     // 1.从最后一位开始,找到第一个可增加的数
			j--;
		}
		k = n - 1;
		while (num[k] < num[j]) {
    
    	// 2.在该可增加的数之后找到一个比它大的数
			k--;
		}
		swap(num[j], num[k]);	// 3.然后交换两数的位置
		j++;
		k = n - 1;
		while (j < k) {
    
    		// 4.然后将两数之间的数进行位置互换(其实就是排序)
			swap(num[j], num[k]);
			j++;
			k--;
		}
	}
	for (int i = 0; i < n - 1; i++) {
    
    
		cout << num[i] << " ";
	}
	cout << num[n - 1] << endl;
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_44338712/article/details/108836743
Recommended