数的全排列和 避免重复运,top之前不入栈,还有求n的数子集,不是全排列了,而是每个数选或不选的问题

来源:互联网 发布:js substring的用法 编辑:程序博客网 时间:2024/06/02 05:11

输入一个数n,下面输入n个数,求n个数的全排列;

代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define Max 50int  book[Max],tt[Max];int a[Max],n;void dfs(long long x ){if(x>n){for(int i=1;i<=n;i++){if(i==n) printf("%d\n",tt[i]);else printf("%d ",tt[i]);}return ;}    for(int i = 1; i<=n; i++)    {        if(!book[i])        {            book[i] = 1;            tt[x] = a[i];            dfs(x+1);            book[i] = 0;        }    }}int main(){    while(~scanf("%d",&n))    {        for(int i=1; i<=n; i++)            scanf("%d",&a[i]);        memset(book,0,sizeof(book));        dfs(1);    }    return 0;}

也可以直接用c++的函数库中的全排列函数 next_permutaton(a,a+n,cmp);

cmp,可加可不加,不加就是全排列,加上,就是自己定义一个cmp函数,按照自己定义的函数里的return条件,所排列;


下面一个n,输入n个数,求n的数的子集

思路:可以用全排列求:但像1,2,3 和 3,2,1 和2,1,3 ...,这些只要其中的一组就行了

所以可以在存到数组中时,加上一个条件,代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define Max 50int  book[Max],tt[Max];int a[Max],n;void dfs(long long x ){    for(int i = 1; i<=n; i++)    {        if(!book[i]&&tt[x-1]<=a[i])        {            book[i] = 1;            tt[x] = a[i];            for(int j = 1;j <=x;j++)            {            if(j==x) printf("%d\n",tt[j]);else printf("%d ",tt[j]);             }            dfs(x+1);            book[i] = 0;        }    }}int main(){    while(~scanf("%d",&n))    {        for(int i=1; i<=n; i++)            scanf("%d",&a[i]);        memset(book,0,sizeof(book));        dfs(1);    }    return 0;}

加上一个条件 a[x-1]<=a[i] ,这个条件使得tt[] 数组中存的数是递增的;这样就避免重复子集的发生;


也可以这样 定义 一个 top 在 top 之前的数,都不在进入栈中这样可以避免重复

和上一个条件的区别就是 上一个输出的数单调递增的,而这个是取决于输入n的数的顺序,在top之前的数是不可以在进栈,不可在进入tt[] 数组中

代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define Max 50int  book[Max],tt[Max];int a[Max],n;void dfs(int top,int x ){    for(int i = top; i<=n; i++)    {        if(!book[i])        {            book[i] = 1;            tt[x] = a[i];            for(int j = 1;j <=x;j++)            {            if(j==x) printf("%d\n",tt[j]);else printf("%d ",tt[j]);             }            dfs(i+1,x+1);            book[i] = 0;        }    }}int main(){    while(~scanf("%d",&n))    {        for(int i=1; i<=n; i++)            scanf("%d",&a[i]);        memset(book,0,sizeof(book));        dfs(1,1);    }    return 0;}


以后避免像1,2,3 或3,2,1或2,1,3等这6种排列,只需要其中的一种就行了,以后就这样,简化上一个代码,仔细想想,top 之前的都不会再入栈了,那么book数组加不加是不是不起作用了,避免重复最简代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define Max 50int  book[Max],tt[Max],num;int a[Max],n;void dfs(int top,int x)    // 为了避免重复,定义了一个top,在top之前的都不在入栈; {                          //  那每个数还用不用标记了,答案是不用了,根本遍历不到在它之前的数,所以根本就不用book[]数组标记了     for(int i = top; i<=n; i++)    {        tt[x] = a[i];        for(int j = 1;j <=x;j++)        {      if(j==x) printf("%d\n",tt[j]);else printf("%d ",tt[j]);         }        dfs(i+1,x+1);     }}int main(){    while(~scanf("%d",&n))    {        for(int i=1; i<=n; i++)            scanf("%d",&a[i]);        memset(book,0,sizeof(book));        dfs(1,1);    }    return 0;}




可以仔细想想, 求子集用不用在每一栈中都遍历n个数,假设这n个数都满足条件,这不是每一栈都要递归n次后,才能结束吗,这时间复杂度太大了, 其实 求子集无非就是这个数选于不选的问题,定义一个光标top,当移动到这数时,这个数就两种情况  选或者不选,每个数的两种情况懂遍历完,不就可以了吗;

代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define Max 50int  book[Max];int a[Max],n;void dfs(int top)    // 这个top可以比作为一个光标,这一行前后移动, { // 移动到这个数,这个数就有两种情况 选或者不选; if(top>n){int f =0;for(int j = 1;j <=n;j++)    {    if(book[j])    {    if(!f){printf("%d",a[j]);f = 1; } else printf(" %d",a[j]);    }}if(f) printf("\n");return ;}book[top] = 1;  //这个数标记一下,选了 dfs(top+1);    book[top] = 0;   //这个数变为不选;     dfs(top+1);}int main(){    while(~scanf("%d",&n))    {        for(int i=1; i<=n; i++)            scanf("%d",&a[i]);        memset(book,0,sizeof(book));        dfs(1);    }    return 0;}



原创粉丝点击