【C++】数字的组合排列情况

一、问题背景

  突发奇想就是想利用递归方法来求解数字的排列组合的问题,感觉数字排列组合使用递归求解起来是比较方便简洁的,我们只需要定义好终止条件以及我们需要剪枝的一些条件。

问题1:给出一个正数n,我们需要求解出0到n-1范围内多个数字能组合成多少个数。例如,n=3,则可以组成:102、120、210、201四种数字组合。
其实通过我们的分析,由于0不能作为数字的首位,其余数字可以位于任何的位置,则最后的排列情况有 n*n! 次。

下面我们通过递归的方式来复杂化求解过程 虽然把问题从简单到复杂,但这也是有助于我们理解递归的过程,可以推导到更加复杂的排列组合情况。

全排列问题 — 解决方案:
#include <iostream>
#include <vector>
#include <string>
using namespace std;

#include <iostream>
#include <vector>
#include <string>
using namespace std;

void _combine(int &res, string str, int start, int n, vector<bool> &used) {
	if (str.length()==n && str[0]!='0') {
	    cout<<str<<endl;
		res += 1;
		return;
	}
	for (int i=0; i<n; i++) {
	    if (!used[i]) {
	        //if(i>0&&nums[i]==nums[i-1]&&!used[i-1])// 这一段代码是用来对所给的排列数字或字母存在重复的进行判重剪枝去掉重复的排列方式。(条件:将开始的序列排序使得相同的字母数字相邻)
            //	continue;
	        used[i] = true;
		    _combine(res, str+to_string(i), start+1, n,used);
		    used[i] = false;
	    }
	  
	}
}
int findNumbers(int n) {
	if (n==0) return 0;
	if (n<=2) return 1;
	int res=0;
	vector<bool> used(n, false);
	_combine(res,"",0,n, used);
	return res;
}
int main()
{
	int res = 0;
	res = findNumbers(4);
    cout << "nums ="<<res<<endl;
    return 0;
}
这种全排列问题我们采用的是回溯的方法来求解,自然而然就让我想到了经典的回溯问题,八皇后的排列问题,跟这个题本质上是一致的,这里我们可以回顾一下:

题目[leetcode 51]
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
攻击的范围是上下左右和四个斜方向在棋盘中的所有位置。

求解方法如下:

#include <bits/stdc++.h>
using namespace std;

void putQueue(int k, int j, vector<vector<int>> &mark) {
	vector<int> x = {0,0,1,-1,1,-1,1,-1};
	vector<int> y = {1,-1,0,0,1,1,-1,-1};
	for (int i=1; i<mark.size(); i++) {
		for (int k=0; k<8; k++) {
			int new_x = k+i*x[k];
			int new_y = j+i*y[k];
			if (new_x >=0&&new_x<mark.size()&&new_y >=0&&new_y<mark.size()) {
				mark[new_x][new_y] = 1;
			}
		}
	}

}

void generate(int k, int n, vector<string> &loc, vector<vector<string>> &res, vector<vector<int>> &mark) {
	if (k==n) {
		res.push_back(loc);
		return;
	}
	for (int i=0; i<n; i++) {
		if(mark[k][i]==0) {
			vector<vector<int>> mark_pre = mark;
			loc[k][i] = 'Q';
			putQueue(k, j, mark);
			generate(k+1, n, loc, res, mark);
			mark = mark_pre;
			loc[k][i] = '.';
		}
	}
}

// 这里我们推广到N皇后的问题
vector<vector<string>> solveNQueens(int n) {
    vector<vector<string>> result; // 保存最终的所有满足情况的结果
    vector<vector<int>> mark(n, vector<int> (n, 0)); // 标记棋盘上还能放置皇后的位置
    vector<string> location; // 保存棋盘上皇后的位置
    for(int i = 0; i < n; i++){ 
        location.push_back("");//棋盘的构造
        location[i].append(n, '.');
    }
    generate(0, n, location, result, mark); //dfs函数调用
    return result;
}



问题2:给定两个整数n和k,从n中返回k个数字的所有可能组合。例如,n=4和k=2的情况下,返回的组合即如果为:
[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4]]

子集 — 解决方案:
#include <iostream>
#include <vector>
#include <string>
using namespace std;


void _combine(vector<vector<int>> &res, vector<int> &tmp, int n, int k, int start) {
	if (tmp.size()==k) {
		res.push_back(tmp);
		return;
	}
	for (int i=start; i<=n; i++) {
		tmp.push_back(i);
		_combine(res, tmp, n, k, i+1);
		tmp.pop_back();
	}
}
vector<vector<int>> combineNums(int n, int k) {
	vector<vector<int>> res;
	vector<int> tmp;
	if (k>n || k==0) return res;
	_combine(res, tmp, n, k, 1);
	return res;
}
int main()
{
	vector<vector<int>> res;
	res = combineNums(4, 2);
	for (int i=0; i<res.size(); i++) {
	    for (int j=0; j<res[0].size(); j++) {
	        cout <<res[i][j]<<" ";
	    }
	    cout<<endl;
	}
    
    return 0;
}

综上所述,有篇文章总结的比较经典:C++ 总结了回溯问题类型 带你搞懂回溯算法(排列篇)
首先我们明白一点,全排列问题子集、组合这是两类问题,我们应该分别对待,总结来说呢就是文章所提的:

“排列”类型问题和“子集、组合”问题不同在于:“排列”问题使用used数组来标识选择列表,而“子集、组合”问题则使用start参数。另外还需注意两种问题的判重剪枝!!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郝同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值