zoukankan      html  css  js  c++  java
  • 全排列

    粗略版,待将来修改。

    https://pan.baidu.com/s/1PYw_ghBe43ry82YtOcKZ7w

    全排列

    目录

    全排列性质... 1

    全排列求法... 2

    Way1(按顺序):... 2

    1-Another1(按顺序):... 4

    1-Another2(不按顺序):... 6

    Way2(按顺序):... 8

    Way3(按顺序):... 10

    Way4(不按顺序):... 12

    各程序运行时间:... 13

    使用... 13

     

    全排列性质

    如n=3:

    123

    132

    213

    231

    312

    321

    对于每个数列,数字1,2,…,n有且仅出现一次。

    对于第一个位置,有n种选择,

    对于第二个位置,不能与第一个位置重复,有(n-1)种选择,

    …,

    对于最后一个位置,有1种选择。

    所有共有n!个数列。

    全排列求法

    1. 字典序顺序从小到大或从大到小

    2. 没有顺序要求

    Way1(按顺序):

    每次确认一个位置的数值,这个数值是以前的位置从未出现的数值。

    该方法由于要从1到n找到符合要求的数值,所以时间复杂度特别高,特别是在处理到数列后期的时候,如173254x,那么x从1找到5都不符合条件,耗费特别多的时间。

    时间复杂度:对于第i位,共有n*(n-1)*…*(n+2-i)种情况能到达第i位,而在第i位,处理的次数都为n次(1~n)。所以总的处理次数:

    n + n*n + n*(n-1)*n + … + n*(n-1)*…*2*n = n*(P(n,0)+P(n,1)+…+P(n,n-1)) = n*n!*(1+1/2+1/6+1/24+…) < n!*n*2

    因为2>1+1/2+1/4+1/8…>1+1/2+1/6+1/24+…。实际上,和约为1.8。

    设T(n)=P(n,0)+P(n,1)+…+P(n,n-1),有T(n)=T(n-1)*n+1。

    P.S.:对于频繁调用函数的方法(保存中间状态,栈压入压出),耗时大,空间消耗也大。

    Code:

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <stdbool.h>  
    4.   
    5. long a[1000],n;  
    6. bool vis[1000];  
    7.   
    8. void dfs(long pos)  
    9. {  
    10. 10.     long i;  
    11. 11.     if (pos==n+1)  
    12. 12.     {  
    13. 13.         for (i=1;i<=n;i++)  
    14. 14.             printf("%ld ",a[i]);  
    15. 15.         printf(" ");  
    16. 16.         return;  
    17. 17.     }  
    18. 18.     for (i=1;i<=n;i++)  
    19. 19.         if (!vis[i])  
    20. 20.         {  
    21. 21.             vis[i]=1;  
    22. 22.             a[pos]=i;  
    23. 23.             dfs(pos+1);  
    24. 24.             vis[i]=0;  
    25. 25.         }  

    26. }  

    1. 27.   

    28. int main()  

    29. {  

    1. 30.     long i;  
    2. 31.     scanf("%ld",&n);  
    3. 32.     for (i=1;i<=n;i++)  
    4. 33.         vis[i]=0;  
    5. 34.     dfs(1);  
    6. 35.     return 0;  

    36. }  

    1-Another1(按顺序):

    同Way1,如果添加一个数据结构记录当前可以使用的数值,那么可以节省判断数值是否可以使用的时间。

    这里使用c++的set,依然是按顺序排列。但实际上由于要保存set的数值,运算次数更多了。

    Code:

    1. #include <cstdio>  
    2. #include <cstdlib>  
    3. #include <cstring>  
    4. #include <cmath>  
    5. #include <set>  
    6. #include <map>  
    7. #include <list>  
    8. #include <queue>  
    9. #include <stack>  

    10. #include <vector>  

    11. #include <algorithm>  

    12. #include <iostream>  

    13. using namespace std;  

    14. const long maxn=1e3+5;  

    1. 15.   

    16. long a[maxn],n;  

    17. set<long>st;  

    1. 18.   

    19. void dfs(long pos)  

    20. {  

    1. 21.     long i;  
    2. 22.     set<long>::iterator j,k;  
    3. 23.     if (pos==n+1)  
    4. 24.     {  
    5. 25.         for (i=1;i<=n;i++)  
    6. 26.             printf("%ld ",a[i]);  
    7. 27.         printf(" ");  
    8. 28.         return;  
    9. 29.     }  
    10. 30.     set<long>st1=st;  
    11. 31.     for (j=st1.begin();j!=st1.end();)  
    12. 32.     {  
    13. 33.         a[pos]=*j;  
    14. 34.         k=j;  
    15. 35.         j++;  
    16. 36.         st.erase(*k);  
    17. 37.         dfs(pos+1);  
    18. 38.         st.insert(a[pos]);  
    19. 39.     }  

    40. }  

    1. 41.   

    42. int main()  

    43. {  

    1. 44.     long i;  
    2. 45.     scanf("%ld",&n);  
    3. 46.     for (i=1;i<=n;i++)  
    4. 47.         st.insert(i);  
    5. 48.     dfs(1);  
    6. 49.     return 0;  

    50. }  

    1-Another2(不按顺序):

    同1-Another1,记录当前可以使用的数值。这里的处理方法是:对于使用过的数,往后放(通过两数交换),使未使用过的数都放在最前面,详见程序。

    时间复杂度:对于“void dfs(long j) ” (j=n,n-1,…,1),执行(j-1)次交换操作。对于第j位,共有n*(n-1)*…*(n+2-j)种情况能到达第i位。所以总的交换次数:

    n-1 + n*(n-2) + n*(n-1)*(n-3) + … + n*(n-1)*…*3*1 = ( n+n*(n-1)+…+n*(n-1)*…*2 ) - ( 1+n+n*(n-1)+…+n*(n-1)*…*(n-3) ) = n!*(1+1/2+1/6+1/24+…) - n!*(1/2+1/6+…) = n!

    原则上比方法1快不少

    Code:

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3.   
    4. long a[1000],n;  
    5.   
    6. void dfs(long j)  
    7. {  
    8.     long i;  
    9.     if (j==0)  
    10. 10.     {  
    11. 11.         for (i=n;i>=1;i--)  //倒序输出  
    12. 12.             printf("%ld ",a[i]);  
    13. 13.         printf(" ");  
    14. 14.         return;  
    15. 15.     }  
    16. 16.     long t;  
    17. 17.     for (i=1;i<j;i++)  
    18. 18.     {  
    19. 19.         //swap(a[i],a[j]) 把a[i]放在最后  
    20. 20.         t=a[i];  
    21. 21.         a[i]=a[j];  
    22. 22.         a[j]=t;  
    23. 23.   
    24. 24.         dfs(j-1);   //可使用的数的数目减1  
    25. 25.   
    26. 26.         //swap(a[i],a[j]) 还原a[i]  
    27. 27.         t=a[i];  
    28. 28.         a[i]=a[j];  
    29. 29.         a[j]=t;  
    30. 30.     }  
    31. 31.     dfs(j-1);   //对于采用a[j],不需要进行交换  

    32. }  

    1. 33.   

    34. int main()  

    35. {  

    1. 36.     long i;  
    2. 37.     scanf("%ld",&n);  
    3. 38.     for (i=1;i<=n;i++)  
    4. 39.         a[i]=i;  
    5. 40.     dfs(n);  
    6. 41.     return 0;  

    42. }  

    Way2(按顺序):

    1~n的全排列若按字典序从小到大排列,

    对于排在第num位的数列(num=0~n-1) {a[1],a[2],…,a[n]},有:

    num=b[1]*(n-1)!+b[2]*(n-2)!+…+b[n-1]*1!

    其中b[k]为从1到a[k]-1(a[k]为第k个数),未出现在a[1]~a[k-1]中的数字数目,如152634,则b[4]=2,因为从1到5(6-1),数字3,4未出现在a[1],a[2],a[3]中。

    证明:

    对于b[k]*(n-k)!,代表的是以a[1],a[2],…,a[k-1]为首的数列集合中,a[1],a[2],…,a[k-1],a[k],…在其中的首位置。如15423,152xx的个数为2!=2个,153xx的个数为2!=2个,以15为首的数列中,154xx在其中的首位置为2!*2=4。

    任意一个b[1],b[2],…,b[n]对应唯一的a[1],a[2],…,a[n],

    而任意一个a[1],a[2],…,a[n]对应唯一的b[1],b[2],…,b[n]。

    任意一个b[1],b[2],…,b[n]对应唯一的num,

    而任意一个num对应唯一的b[1],b[2],…,b[n]。

    时间复杂度:对于b[k],找到对应的a[k]就需要进行b[k]次寻找后继操作(见下文代码)。而b[k]的值为0,1,…,n-k的可能性是相同的,所以对于b[k],平均操作次数为(n-k)/2,总的寻找后继操作为:n!*( (n-1)/2+(n-2)/2+…+1/2 )

    = n!*(n-1)*n/4。

    Code:

    1. //一个数指向下一个可以使用的数,可以跳过查找那些已经使用过的数  
    2. #include <stdio.h>  
    3. #include <stdlib.h>  
    4.   
    5. long a[1000],n,next[1000],c[1000];  
    6.   
    7. int main()  
    8. {  
    9.     long i,j,k,l,num,g,max_num;  
    10. 10.     scanf("%ld",&n);  
    11. 11.     c[n]=1; c[n-1]=1;  
    12. 12.     for (i=n-2;i>=1;i--)  
    13. 13.         c[i]=c[i+1]*(n-i);  
    14. 14.     max_num=1;  
    15. 15.     for (i=2;i<=n;i++)  
    16. 16.         max_num*=i;  
    17. 17.     for (i=0;i<max_num;i++)  
    18. 18.     {  
    19. 19.         for (j=0;j<n;j++)  
    20. 20.             next[j]=j+1;  
    21. 21.         num=i;  
    22. 22.         for (j=1;j<=n;j++)  
    23. 23.         {  
    24. 24.             g=num/c[j];  
    25. 25.             l=0;  
    26. 26.             for (k=0;k<g;k++)  
    27. 27.                 l=next[l];  
    28. 28.             printf("%ld ",next[l]);  
    29. 29.             next[l]=next[next[l]];  
    30. 30.             num=num%c[j];  
    31. 31.         }  
    32. 32.         printf(" ");  
    33. 33.     }  
    34. 34.     return 0;  

    35. }  

    Way3(按顺序):

    字典序从小到大排序,根据第num个数列生成第num+1个数列

    如426531->431256,

    方法:

    1. 从右到左,找到第一个满足数值比 与之右相邻的数 大的数,这个数设为a[k]。如3>1,5>3,6>5,2<6,所以2满足条件。

    2. 找到在a[k+1]~a[n]中比a[k]大的最小的数a[l],用于代替a[k];如数字3大于数字2,且是6,5,3,1中比2大的数中数字最小的,所以数字3代替数字2。

    3. a[k+1]~a[n]按照从小到大的顺序排列。如剩下的数从小到大排列为1,2,5,6,分别放置于第3,4,5,6位。

    时间复杂度:若从右到左有k个大于关系,直到遇到最小关系。第2,3步的时间复杂度约为k,而第1步的时间复杂度约为k。总时间复杂度:

    O(n+n*(n-1)*2+n*(n-1)*(n-2)*3+)

    Code:

    1. //交换数字时,要合理安排先后顺序,从而减少辅助变量的使用。  
    2. #include <stdio.h>  
    3. #include <stdlib.h>  
    4.   
    5. long a[1000],n;  
    6.   
    7. int main()  
    8. {  
    9.     long i,j,k,l,t,max_num;  
    10. 10.     scanf("%ld",&n);  
    11. 11.     max_num=1;  
    12. 12.     for (i=2;i<=n;i++)  
    13. 13.         max_num*=i;  
    14. 14.     for (i=1;i<=n;i++)  
    15. 15.         a[i]=i;  
    16. 16.     for (i=0;i<max_num;i++)  
    17. 17.     {  
    18. 18.         for (j=1;j<=n;j++)  
    19. 19.             printf("%ld ",a[j]);  
    20. 20.         printf(" ");  
    21. 21.         for (j=n-1;j>=1;j--)  
    22. 22.             if (a[j]<a[j+1])  
    23. 23.                 break;  
    24. 24.         l=j+n+1;  
    25. 25.         for (k=j+1;k<=l/2;k++)  
    26. 26.         {  
    27. 27.             t=a[k];  
    28. 28.             a[k]=a[l-k];  
    29. 29.             a[l-k]=t;  
    30. 30.         }  
    31. 31.   
    32. 32.         for (k=j+1;k<=n;k++)    //其实用二分更快(如果n数值大)  
    33. 33.             if (a[k]>a[j])  
    34. 34.             {  
    35. 35.                 t=a[j];  
    36. 36.                 a[j]=a[k];  
    37. 37.                 a[k]=t;  
    38. 38.                 break;  
    39. 39.             }  
    40. 40.     }  

    41. }  

    Way4(不按顺序):

    处理到第k位时,让之前未使用过的数都有机会在第k位上,详见程序。代码与1-Another2很像。

    证明:

    若之前的前k位是正确的,证明前k+1位也是正确的。

    对于任意一个前k+1位,必定是由它的前k位的前缀生成而成的,而这个前缀有且只有一位,而且在这个前缀下,根据a[k+1]与a[r+1]的交换,必定可以生成这个前k+1位。

    时间复杂度:O(n),如1-Another2。

    Code:

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3.   
    4. long a[1000],n;  
    5.   
    6. void dfs(long j)  
    7. {  
    8.     long i;  
    9.     if (j==n+1)  
    10. 10.     {  
    11. 11.         for (i=1;i<=n;i++)  
    12. 12.             printf("%ld ",a[i]);  
    13. 13.         printf(" ");  
    14. 14.         return;  
    15. 15.     }  
    16. 16.     long t;  
    17. 17.     dfs(j+1);  
    18. 18.     for (i=j+1;i<=n;i++)  
    19. 19.     {  
    20. 20.         t=a[i];  
    21. 21.         a[i]=a[j];  
    22. 22.         a[j]=t;  
    23. 23.   
    24. 24.         dfs(j+1);  
    25. 25.   
    26. 26.         t=a[i];  
    27. 27.         a[i]=a[j];  
    28. 28.         a[j]=t;  
    29. 29.     }  

    30. }  

    1. 31.   

    32. int main()  

    33. {  

    1. 34.     long i;  
    2. 35.     scanf("%ld",&n);  
    3. 36.     for (i=1;i<=n;i++)  
    4. 37.         a[i]=i;  
    5. 38.     dfs(1);  

    39. }  

    各程序运行时间:

    使用

    让处理、判断蕴含在全排列中

    1. 八皇后问题

    Problem:

    n*n矩阵,放置n个皇后,使皇后之间不在同一个行、列、对角线。

    Solution:

    每一行,每一列有且仅有一个皇后。

    以皇后在第1行,第2行,…,第n行所在的列数构成数列,数列一定是全排列的其中一个数列。

    另外还有对角线这个约束条件。

    Code:

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <stdbool.h>  
    4.   
    5. long a[10],n;  
    6. long vis[10][10];  
    7. /*若一个格子被一个皇后影响(行、列、对角线),则该格子数值加1;当该格子数值大于1时,则不合理*/  
    8.   
    9. long min(long a,long b)  

    10. {  

    1. 11.     if (a>b)  
    2. 12.         return b;  
    3. 13.     else  
    4. 14.         return a;  

    15. }  

    1. 16.   

    17. long max(long a,long b)  

    18. {  

    1. 19.     if (a>b)  
    2. 20.         return a;  
    3. 21.     else  
    4. 22.         return b;  

    23. }  

    1. 24.   

    25. void dfs(long x)  

    26. {  

    1. 27.     long i;  
    2. 28.     if (x==n+1)  
    3. 29.     {  
    4. 30.         for (i=1;i<=n;i++)  
    5. 31.             printf("%ld ",a[i]);  
    6. 32.         printf(" ");  
    7. 33.         return;  
    8. 34.     }  
    9. 35.     long j,t,y;  
    10. 36.     for (i=x;i<=n;i++)  
    11. 37.         {  
    12. 38.             t=a[i];  
    13. 39.             a[i]=a[x];  
    14. 40.             a[x]=t;  
    15. 41.             y=a[x];  
    16. 42.   
    17. 43.             if (vis[x][y]==0)  
    18. 44.             {  
    19. 45.                 //列  
    20. 46.                 for (j=1;j<=n;j++)  
    21. 47.                     vis[j][y]++;  
    22. 48.                 //对角线1  
    23. 49.                 t=n-max(x,y);  
    24. 50.                 for (j=-min(x,y)+1;j<=t;j++)  
    25. 51.                     vis[x+j][y+j]++;  
    26. 52.                 //对角线2  
    27. 53.                 t=min(n-x,y-1);  
    28. 54.                 for (j=max(1-x,y-n);j<=t;j++)  
    29. 55.                     vis[x+j][y-j]++;  
    30. 56.                 vis[x][y]-=2;  
    31. 57.   
    32. 58.                 dfs(x+1);  
    33. 59.   
    34. 60.                 //列  
    35. 61.                 for (j=1;j<=n;j++)  
    36. 62.                     vis[j][y]--;  
    37. 63.                 //对角线1  
    38. 64.                 t=n-max(x,y);  
    39. 65.                 for (j=-min(x,y)+1;j<=t;j++)  
    40. 66.                     vis[x+j][y+j]--;  
    41. 67.                 //对角线2  
    42. 68.                 t=min(n-x,y-1);  
    43. 69.                 for (j=max(1-x,y-n);j<=t;j++)  
    44. 70.                     vis[x+j][y-j]--;  
    45. 71.                 vis[x][y]+=2;  
    46. 72.             }  
    47. 73.   
    48. 74.             t=a[i];  
    49. 75.             a[i]=a[x];  
    50. 76.             a[x]=t;  
    51. 77.         }  

    78. }  

    1. 79.   

    80. int main()  

    81. {  

    1. 82.     long i;  
    2. 83.     scanf("%ld",&n);  
    3. 84.     for (i=1;i<=n;i++)  
    4. 85.         a[i]=i;  
    5. 86.     dfs(1);  
    6. 87.     return 0;  

    88. }  

    2. 任务分配

    Problem:

    n个任务,n个人,每人分配一个任务,任意一个任务有且仅被一个人选取。求最小总代价。

    Solution1:

    全排列

    剪枝:唯有目前的代价小于最小总代价时,才继续搜索

    Code1:

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #define inf 1e9  
    4.   
    5. long mincost=inf,n;  
    6. long c[50][50],a[50],result[50];  
    7. //c[j][i]:第i个人分配第i个项目的代价  
    8.   
    9. void dfs(long j,long cost)  

    10. {  

    1. 11.     long i;  
    2. 12.     if (j==n+1)  
    3. 13.     {  
    4. 14.         if (cost<mincost)  
    5. 15.         {  
    6. 16.             mincost=cost;  
    7. 17.             for (i=1;i<=n;i++)   //输出其中一个方案  
    8. 18.                 result[i]=a[i];  
    9. 19.         }  
    10. 20.     }  
    11. 21.     long t;  
    12. 22.     for (i=j;i<=n;i++)  
    13. 23.     {  
    14. 24.         t=a[i];  
    15. 25.         a[i]=a[j];  
    16. 26.         a[j]=t;  
    17. 27.   
    18. 28.         if (cost+c[j][a[j]]<mincost) //剪枝  
    19. 29.             dfs(j+1,cost+c[j][a[j]]);  
    20. 30.   
    21. 31.         t=a[i];  
    22. 32.         a[i]=a[j];  
    23. 33.         a[j]=t;  
    24. 34.     }  

    35. }  

    1. 36.   

    37. int main()  

    38. {  

    1. 39.     long i,j;  
    2. 40.     scanf("%ld",&n);  
    3. 41.     for (i=1;i<=n;i++)  
    4. 42.         for (j=1;j<=n;j++)  
    5. 43.             scanf("%ld",&c[i][j]);  
    6. 44.     for (i=1;i<=n;i++)  
    7. 45.         a[i]=i;  
    8. 46.     dfs(1,0);  
    9. 47.     printf("%ld ",mincost);  
    10. 48.     for (i=1;i<=n;i++)  
    11. 49.         printf("%ld ",result[i]);  
    12. 50.     return 0;  

    51. }  

    52. /* 

    53. 4 

    54. 9 2 7 8 

    55. 6 4 3 7 

    56. 5 8 1 8 

    57. 7 6 9 4 

    58. */  

    3. 哈密顿回路

    Solution1:

    同任务分配,求全排列,加上连接起点和终点的边。

    Code1:

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #define inf 1e9  
    4.   
    5. long mincost=inf,n;  
    6. long road[50][50],a[50],result[50];  
    7. //road[i][j]:编号为i的点到编号为j的的代价  
    8.   
    9. void dfs(long j,long cost)  

    10. {  

    1. 11.     long i;  
    2. 12.     if (j==n+1)  
    3. 13.     {  
    4. 14.         if (cost+road[a[n]][a[1]]<mincost)  
    5. 15.         {  
    6. 16.             mincost=cost+road[a[n]][a[1]];  
    7. 17.             for (i=1;i<=n;i++)   //输出其中一个方案  
    8. 18.                 result[i]=a[i];  
    9. 19.         }  
    10. 20.     }  
    11. 21.     long t;  
    12. 22.     for (i=j;i<=n;i++)  
    13. 23.     {  
    14. 24.         t=a[i];  
    15. 25.         a[i]=a[j];  
    16. 26.         a[j]=t;  
    17. 27.         //road[a[j-1]][a[j]]:第j-1个点到第j个点的的代价  
    18. 28.         if (cost+road[a[j-1]][a[j]]<mincost) //剪枝  
    19. 29.             dfs(j+1,cost+road[a[j-1]][a[j]]);  
    20. 30.   
    21. 31.         t=a[i];  
    22. 32.         a[i]=a[j];  
    23. 33.         a[j]=t;  
    24. 34.     }  

    35. }  

    1. 36.   

    37. int main()  

    38. {  

    1. 39.     long i,j;  
    2. 40.     scanf("%ld",&n);  
    3. 41.     for (i=1;i<=n;i++)  
    4. 42.         for (j=1;j<=n;j++)  
    5. 43.             scanf("%ld",&road[i][j]);  
    6. 44.     a[0]=0;  
    7. 45.     for (i=1;i<=n;i++)  
    8. 46.         road[0][i]=0;  
    9. 47.     for (i=1;i<=n;i++)  
    10. 48.         a[i]=i;  
    11. 49.     dfs(1,0);  
    12. 50.     printf("%ld ",mincost);  
    13. 51.     for (i=1;i<=n;i++)  
    14. 52.         printf("%ld ",result[i]);  
    15. 53.     return 0;  

    54. }  

    55. /* 

    56. 4 

    57. 9 2 7 8 

    58. 6 4 3 7 

    59. 5 8 1 8 

    60. 7 6 9 4 

    61. */  

    Solution2:

    与任务分配不同,哈密顿回路的第i个点与第i+1个点有关联,影响着哈密顿回路的数值。

    任务分配:f(x)       x为已经分配的任务集合

    哈密顿回路:f(x,y)   x为已经经过的点集合,y为目前所在的点

    任务分配的f(x)具有唯一值,但哈密顿回路不是这样。

  • 相关阅读:
    POJ测试数据合集
    POJ1724ROADS
    关闭进程的数据库
    config上传设置
    tfs 撤销挂起的更改
    cn_visual_studio_team_foundation_server_2010_x86_x64_dvd_531909
    js 中文转义
    文件下载乱码
    杀死数据库进程
    Python基础综合练习
  • 原文地址:https://www.cnblogs.com/cmyg/p/9567182.html
Copyright © 2011-2022 走看看