ACM:回溯法,枚举排列

(一)生成1~n的排列

分析:用递归的思想解决:先输出所有以1开头的排列(这一步是递归调用),然后输出以2开头的排列(又是递归调用),接着是以3开头的排列......最后才是以n开头的排列。

伪代码:

void print_permutation(序列A, 集合S) {

if(S为空) 输出序列A;

else 按照从小到大的顺序依次考虑S的每个元素v {

print_permutation(在A的末尾填加v后得到的新序列,S-{v});

下面是实现代码:

#include <iostream>
using namespace std;

const int MAXN = 1000;
int A[MAXN], n;

void print_permutation(int n, int *A, int cur) {
	if(cur == n) {    //递归边界
		for(int i = 0; i < n; ++i) cout << A[i] << " ";
		cout << endl;
	}else {
		for(int i = 1; i <= n; ++i) {    //尝试在A[cur]中填各种整数i
			int ok = 1;
			for(int j = 0; j < cur; ++j) {
				if(A[j] == i) ok = 0;   //如果i已经在A[0]~A[cur-1]出现过,则不能再选
			}
			if(ok) {   //i在A[0]~A[cur-1]<span style="font-family: Arial, Helvetica, sans-serif;">中没有出现过,所以可以把i放进A[cur]</span>

				A[cur] = i;
				print_permutation(n, A, cur+1);  //递归调用
			}
		}
	}
}

int main() {
	cin >> n;
	print_permutation(n, A, 0);
	return 0;
}

(二)生成可重集的排列

输入数组P,按照字典序输出数组P各元素的所有全排列。

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1000;
int A[MAXN], P[MAXN], n;

void print_permutation(int n, int *P, int *A, int cur) {
	if(cur == n) {
		for(int i = 0; i < n; ++i) cout << A[i] << " ";
		cout << endl;
	}else for(int i = 0; i < n; ++i) if(!i || P[i] != P[i-1]){  //if条件句确保了不会重复递归调用相同的元素,保证了我们枚举的下标i应不重复不遗漏地取遍所有P[i]值
		int c1 = 0, c2 = 0;
		for(int j = 0; j < cur; ++j) if(P[i] == A[j]) c1++;  //A[0]~A[cur-1]中P[i]出现的次数放在c1中
		for(int j = 0; j < n; ++j) if(P[i] == P[j]) c2++;  //P数组中P[i]的出现次数放在c2中。
		if(c1 < c2) {    //只要c1<c2就能递归调用,这样就保证了不会遗漏相同的元素。
			A[cur] = P[i];
			print_permutation(n, P, A, cur+1);
		}
	}
}

int main() {
	cin >> n;
	for(int i = 0; i < n; ++i) cin >> P[i];
	sort(P, P+n);
	print_permutation(n, P, A, 0);
	return 0;
}

(三)生成下一个排列:

枚举所有排列的另一个方法是从字典序最小排列开始,不停的调用“求下一个排列”的过程。

利用STL里面的next_permutation来求下一个排列,

#include <iostream>
#include <cstdio>
#include <algorithm>

#define MAXN 1000

using namespace std;

int P[MAXN];

int main(){
	int n;
	cin >> n;
	for(int i = 0; i < n; ++i){
		cin >> P[i];
	}
	sort(P, P+n);   // 排序,得到p的最小排列
	do{
		for(int i = 0; i < n; ++i){
			cout << P[i] << " ";
		}
		cout << endl;
	}while(next_permutation(P, P+n));
	return 0;
}

ACM:回溯法,枚举排列

时间: 2024-12-28 00:45:49

ACM:回溯法,枚举排列的相关文章

八皇后问题(回溯法&amp;枚举法)

作者 : 卿笃军 本文讨论了八皇后问题的三种解决方案: 一.枚举法 二.回溯法(递归版) 三.回溯法(非递归版) 本来这些代码是以前编写好的,没有发表,由于最近又学习到了八皇后问题,自己整理了一下发表了出来! 首先.说明一下何为八皇后问题,我也不去谷歌了,直接简单的说明一下: 八皇后问题,就是在一个8*8的平面棋盘上,要求你摆放8个棋子,要求:这8个棋子不能有2个在同一行,也不能有2个在同一列,同时一条斜线上面也不能有2个~~~~ 比如:4*4的棋盘,你可以这样摆放(4皇后问题): 以上图为参照

回溯法

递归回溯 由于回溯法是对解空间的深度优先搜索,因此在一般情况下可用递归函数来实现回溯法如下: t表示递归深度,即当前扩展节点在解空间树的深度. n用来控制递归深度.当t>n时表示算法搜索到叶节点. void backtrack( int t ) { if ( t>n ) output(x); else for( int i=f(n,t); i<=g(n,t); i++ ) { x[t]=h(i); if ( constraint(t)&&bound(t) ) backtr

回溯法求装载问题

1.回溯法 (1)描述:回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法.  (2)原理: 回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树.算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解.如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯:否则,进入该子树,继续按深度优先策略搜索. 回溯法的基本做法是搜索,或是一种组织得井井有条的,

回溯法——批处理作业调度

问题描述: 给定n个作业的集合J=(J1,J2,... ,Jn).每一个作业Ji都有两项任务分别在2台机器上完成.每个作业必须先有机器1处理,然后再由机器2处理.作业Ji需要机器j的处理时间为tji.对于一个确定的作业调度,设Fji是作业i在机器j上完成处理时间.则所有作业在机器2上完成处理时间和f=F2i,称为该作业调度的完成时间和. 简单描述: 对于给定的n个作业,指定最佳作业调度方案,使其完成时间和达到最小. 举例说明: tji 机器1 机器2 作业1 2 1 作业2 3 1 作业3 2

回溯法小实例

1.图的m着色问题: 1 /* 2 *问题描述:给定无向连通图G和m种不同的颜色.用这些颜色为图G的各个顶点着色,每个顶点着一种颜色.是否有一种着色法使G中每条边的两个顶点着不同的颜色. 3 * 这个问题是图的m可着色判定问题.若一个图最少需要m中颜色才能使图中每条边连接的2个顶点着不同的颜色,则称这个数m为该图的色数. 4 *算法分析:给定图G=(V,E)和m中颜色,如果这个图不是m可着色,给出否定回答:如果这个图是m可着色的,找出所有不同的着色法. 5 * 回溯法+子集树 6 * 问题的解空

算法复习笔记(回溯法,分支限界法)

回溯法 分支限界法 回溯法 回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法. 基本思想: 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树.当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯.(其实回溯法就是对隐式图的深度优先搜索

回溯法 子集树和排序树

当所给问题是从n个元素的集合S中找出满足某种性质的子集时,解空间为子集树.例如:0-1背包问题 当所给问题是从n个元素的集合S中找出满足某种性质的排列时,解空间为排列树.例如:旅行售货员问题 回溯法搜索子集树算法描述为: void backtrack(int  t) { if(t>n) output(x); else for(int i=0; i<=1; i++) { x[t] = i; if(constraint(t) && bound(t)) backtrack(t+1);

回溯法的解空间表示方法

回溯法解题时通常包含3个步骤: 1. 针对所给问题,定义问题的解空间: 2. 确定易于搜索的解空间结构: 3. 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索. 对于问题的解空间结构通常以树或图的形式表示,常用的两类典型的解空间树是子集树和排列树.当所给的问题是从n个元素的集合S中找到S满足某种性质的子集时,相应的解空间树称为子集树.例如,n个物品的0-1背包问题所对应的解空间树是一棵子集树,这类子集树通常有2**n个叶结点,遍历子集树的算法需要O(2**n)计算时间.当所给问题

ACM:回溯法,八皇后问题,素数环

(一)八皇后问题 (1)回溯法 #include <iostream> #include <string> #define MAXN 100 using namespace std; int tot = 0, n = 8; int C[MAXN]; void search(int cur) { if(cur == n) ++tot; //递归边界,只要走到了这里,所有皇后必然不冲突 else for(int i = 0; i < n; ++i) { int ok = 1; C