/********
*给你一个数n,输出1到n的全排列
*深度优先搜索
********/   
  
  
#include <stdio.h>  
#include <stdlib.h>  
int book[10], a[10], n;  
  
void dfs(int step)  
{  
int i;  
if(step == n+1)//当你在第n+1步的时候,说明前n部已经排好了。   
    {  
for(i = 1; i <= n; i++)  
"%d ", a[i]);  
"\n");  
          
return ;//返回之前的一步;   
    }  
  
for(i = 1; i <= n; i++)//按照1,2,3.。。的方式一一尝试。   
    {  
if(book[i]==0)//判断扑克牌i是不是还在手里   
        {  
//将i牌放在第step个盒子里。   
//表示扑克牌不再第step个盒子里   
//继续下一步。   
//将刚才尝试的扑克收回,才能进行下一步的尝试。   
        }  
    }  
return ;//结束搜索。   
}  
int main()  
{  
while(~scanf("%d", &n))  
        dfs(1);  
      
return 0;  
 }

    

-------------------------------------------------------分割线---------------------------------------------------------------

全排列:

全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。

从集合中依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理,从而得到所有元素的全排列。

以对字符串abc进行全排列为例,我们可以这么做:以abc为例
固定a,求后面bc的排列:abc,acb,求好后,a和b交换,得到bac
固定b,求后面ac的排列:bac,bca,求好后,c放到第一位置,得到cba
固定c,求后面ba的排列:cba,cab。

这个思想和回溯法比较吻合。

代码可如下编写所示

// 回溯法搜索全排列树  
  
#include<stdio.h>  
  
#define M 20  
int n;  
int a[M];  
int cnt = 0;// 记录全排列个数  
  
void swap(int *a, int *b)//交换a,b  
{  
char t;  
    t = *a;  
    *a = *b;  
    *b = t;  
}  
  
void dfs(int cur)  
{  
int i;  
if(cur == n)// 找到 输出全排列  
    {  
        ++cnt;  
for(i=0; i<n; i++)  
"%d ", a[i]);  
"\n");  
    }  
else  
    {  
// 将集合中的所有元素分别与第一个交换,这样就总是在  
// 处理剩下元素的全排列(即用递归)  
for(i=cur; i<n; i++)  
        {  
            swap(&a[cur], &a[i]);  
            dfs(cur+1);  
//回溯  
        }  
    }  
}  
  
  
int main()  
{  
while(scanf("%d", &n) != EOF)  
    {  
for(int i=0; i<n; i++)  
// 假设集合S为:1 2 3 ... n  
        cnt = 0;  
        dfs(0);  
"count:%d\n", cnt);  
    }  
return 0;  
}


或者利用一个vis数组标识每个元素是否已经被访问,代码如下:

#include <stdio.h>  
  
int a[10];  
bool vis[10];  
int n;//排列个数 n  
void dfs(int dep)  //打印所有的全排列,穷举每一种方案  
{  
if(dep == n)  
    {  
for(int i = 0; i < n; i++)  
        {  
"%d ",a[i]);  
        }  
"\n");  
return ;  
    }  
for(int i = 0; i < n; i++)// 找一个最小的未标记的数字,保证了字典序最小  
    {  
if(!vis[i])  
        {  
            a[dep] = i+1;  
true;// 找到了就标记掉,继续下一层  
            dfs(dep + 1);  
false;  
        }  
    }  
}  
int main()  
{  
while(scanf("%d",&n) != EOF)  
    {  
        dfs(0);  
    }  
return 0;  
}


子集构造:

从n个元素的集合S中找出S满足某种性质的子集时,相应解空间树称为子集树。

求n个元素集合的子集,如A = {1, 2, 3}则A集合的子集有:    P(A) = {{1,2,3}, {1,2}, {1,3},{1},{2,3},{2},{3},{}}

采用位向量法,构造一个位向量vis[], vis[i] = 1 表示i在子集A中。

代码如下:

#include<stdio.h>  
  
#define M 20  
int n;  
int a[M];  
int vis[M];  
int cnt = 0;// 记录子集个数  
  
  
void dfs(int cur)  
{  
int i;  
if(cur == n)// 找到 输出所有子集  
    {  
        ++cnt;  
int flag =1;  
for(i=0; i<n; i++)  
if(vis[i])  
            {  
"%d ", a[i]);  
                flag = 0;  
            }  
if(flag)//子集中的空集  
"φ");  
"\n");  
    }  
else  
    {  
for(i=1; i>=0; --i)//vis 中分为 选or不选即 1,0  
        {  
            vis[cur] = i;  
            dfs(cur+1);  
        }  
    }  
}  
  
  
int main()  
{  
while(scanf("%d", &n) != EOF)  
    {  
for(int i=0; i<n; i++)  
// 假设集合S为:1 2 3 ... n  
        cnt = 0;  
        dfs(0);  
"count:%d\n", cnt);  
    }  
return 0;  
}


此外可以采用采用增量构造法,


代码如下:


#include<stdio.h>  
  
#define M 20  
int n;  
int a[M];  
int vis[M];  
int cnt = 0;// 记录子集个数  
  
  
void subset(int cur, int A[])  
{  
int i;  
    ++cnt;  
if(0 == cur)//子集中的空集  
"φ");  
for(i=0; i<cur; ++i)// 打印当前集合  
    {  
"%d ", A[i]);  
    }  
"\n");  
      
int min  = cur ? A[cur-1]:0;//确定当前元素的最小可能值  
for(i=min; i<n; ++i)  
    {  
        A[cur] = a[i];  
//递归构造  
    }  
}  
  
  
int main()  
{  
while(scanf("%d", &n) != EOF)  
    {  
for(int i=0; i<n; i++)  
// 假设集合S为:1 2 3 ... n  
        cnt = 0;  
int A[M]={0};  
        subset(0, A);  
"count:%d\n", cnt);  
    }  
return 0;  
}