求排列
1.求全排列
void print_permutation(int n,int* A,int cur){
if(cur==n){ //递归边界
for(int i = 0;i<n;i++) printf("%d",A[i]);
printf("\n");
}
else for(int i = 1;i<=n;i++){
int ok = 1;
for(int j=0;j<cur;j++)
if(A[j]==i) ok = 0;
if(ok){
A[cur] = i;
print_permutation(n,A,cur+1);
}
}
}
2.stl里的两个函数
next_permutation(p,p+n);
prev_permutation(p,p+n);
子集生成
1.增量构造法
void print_subset(int n,int* A,int cur){
for(int i = 0;i<cur;i++) printf("%d",A[i]);//打印当前集合
printf("\n");
int s = cur?A[cur-1]+1:0; //确定当前元素的最小可能值
for(int i=s;i<n;i++){
A[cur] = i;
print_subset(n,A,cur+1);
}
}
上述代码中,s变量和下面的循环用来定序,将集合按顺序排列,就不会把{1,2}和{2,1}都输出了。
2.位向量法
void print_subset(int n,int* B,int cur){
if(cur==n){
for(int i = 0; i<cur ;i++)
if(B[i]) printf("%d",i);
printf("\n");
return;
}
B[cur] = 1;
print_subset(n,B,cur+1);
B[cur] = 0;
print_subset(n,B,cur+1);
}
这种方法的思路是用B数组来标记每个元素,cur用来计数,三个位置都被标记时才输出。
3.二进制法
void print_subset(int n,int s)
{
for(int i=0;i<n;i++)
if(s&(1<<i)) printf("%d ",i);
printf("\n");
}
for(int i=0;i<(1<<n);i++)
print_subset(n,i);
思路是将各子集编码,然后用位运算取每个子集,手动写一下就发现原理与位向量法有异曲同工之妙。
回溯法常见用法
以八皇后部分代码为例
vis[0][1] = vis[1][cur+1] = vis[2][cur-i+n] = 1;
search(cur+1);
vis[0][1] = vis[1][cur+1] = vis[2][cur-i+n] = 0;
第一句的意思是放置了皇后,然后第二行继续递归测试下一行,第三行是递归返回上一层后的节点处,要把之前放置此处的皇后拿走,否则肯定得不出结果。
类似这三行的代码常出现在递归/回溯解决问题的代码中,我认为这是回溯解决问题的一个小关键点所在。