zoukankan      html  css  js  c++  java
  • 排列组合与回溯算法

    原文:http://blog.csdn.net/kbsoft/archive/2003/11/13/10294.aspx

    [
    关键字] 递归 DFS

    [前言] 这篇论文主要针对排列组合对回溯算法展开讨论,在每一个讨论之后,还有相关的推荐题。在开始之前,我们先应该看一下回溯算法的概念,所谓回溯:就是搜索一棵状态树的过程,这个过程类似于图的深度优先搜索(DFS),在搜索的每一步(这里的每一步对应搜索树的第i层)中产生一个正确的解,然后在以后的每一步搜索过程中,都检查其前一步的记录,并且它将有条件的选择以后的每一个搜索状态(即第i+1层的状态节点)。

    需掌握的基本算法:

    排列:就是从n个元素中同时取r个元素的排列,记做P(n,r)。(当r=n时,我们称P(n,n)=n!为全排列)例如我们有集合OR = {1,2,3,4},那么n = |OR| = 4,切规定r=3,那么P(4,3)就是:

    {1,2,3}; {1,2,4}; {1,3,2}; {1,3,4};{1,4,2};{1,4,3};{2,1,3};{2,1,4}; {2,3,1}; {2,3,4}; {2,4,1}; {2,4,3}; {3,1,2}; {3,1,4}; {3,2,1}; {3,2,4}; {3,4,1}; {3,4,2}; {4,1,2}; {4,1,3}; {4,2,1}; {4,2,3}; {4,3,1}; {4,3,2}

    算法如下:

    int  n, r;
    char used[MaxN];
    int  p[MaxN];
     
    void permute(int pos)
    { int i;
    /*如果已是第r个元素了,则可打印r个元素的排列 */
        if (pos==r) {
            for (i=0; i<r; i++)
                cout << (p[i]+1);
            cout << endl;
            return;
        }
        for (i=0; i<n; i++)
            if (!used[i]) { /*如果第i个元素未用过*/
    /*使用第i个元素,作上已用标记,目的是使以后该元素不可用*/
                used[i]++;
    /*保存当前搜索到的第i个元素*/
                p[pos] = i;
    /*递归搜索*/
               permute(pos+1);
     
    /*恢复递归前的值,目的是使以后改元素可用*/
     used[i]--;
            }
    }

    相关问题

    UVA 524 Prime Ring Problem

     

    可重排列就是从任意n个元素中r个可重复的元素的排列。例如,对于集合OR={1,1,2,2}, n = |OR| = 4, r = 2,那么排列如下:

    {1,1}; {1,2}; {1,2}; {1,1}; {1,2}; {1,2}; {2,1}; {2,1}; {2,2}; {2,1}; {2,1}; {2,2}

    则可重排列是:

    {1,1}; {1,2}; {2,1}; {2,2}.

    算法如下:

    #define FREE -1
    int n, r;
    /*使元素有序*/
    int E[MaxN] = {0,0,1,1,1};
    int P[MaxN];
    char used[MaxN];
     
    void permute(int pos)
    {
    int i;
    /*如果已选了r个元素了,则打印它们*/
        if (pos==r)  {
            for (i=0; i<r; i++)
                cout << P[i];
            cout << endl;
            return;
        }
    /*标记下我们排列中的以前的元素表明是不存在的*/
        P[pos] = FREE;
        for (i=0; i<n; i++)
    /*如果第I个元素没有用过,并且与先前的不同*/
            if (!used[i] && E[i]!=P[pos]) {
    /*使用这个元素*/
                used[i]++;
    /*选择现在元素的位置*/
                P[pos] = E[i];
    /*递归搜索*/
                permute(pos+1);
    /*恢复递归前的值*/
                used[i]--;
            }
    }

    相关习题

    UVA 10098 Generating Fast, Sorted Permutations

     

    组合:n个不同元素中取r个不重复的元素组成一个子集,而不考虑其元素的顺序,称为从n个中取r个的无重组合,例如OR = {1,2,3,4}, n = 4, r = 3则无重组合为:

    {1,2,3}; {1,2,4}; {1,3,4}; {2,3,4}.

    算法如下:

    int n, r;
    int C[5];
    char used[5];
     
    void combine(int pos, int h)
    {
    int i;
    /*如果已选了r个元素了,则打印它们*/
        if (pos==r) {
            for (i=0; i<r; i++)
                cout<< C[i];
            cout<< endl;
            return;
        }
        for (i=h; i<=n-r+pos; i++) /*对于所有未用的元素*/
            if (!used[i]) {
    /*把它放置在组合中*/
                C[pos] = i;
    /*使用该元素*/
     used[i]++;
    /*搜索第i+1个元素*/
         combine(pos+1,i+1);
    /*恢复递归前的值*/
     used[i]--;
            }
    }

    相关问题:

    Ural 1034 Queens in peaceful position

     

    可重组合:类似于可重排列。

    [] 给出空间中给定n(n<10)个点,画一条简单路径,包括所有的点,使得路径最短。

    :这是一个旅行售货员问题TSP。这是一个NP问题,其实就是一个排列选取问题。

    算法如下:

    int  n, r;
    char used[MaxN];
    int  p[MaxN];
    double min;
     
    void permute(int pos, double dist)
    {
    int i;
        if (pos==n) {
            if (dist < min) min = dist;
            return;
        }
        for (i=0; i<n; i++)
            if (!used[i]) {
                used[i]++;
                p[pos] = i;
               if (dist + cost(point[p[pos-1]], point[p[pos]]) < min)
                    permute(pos+1, dist + cost(point[p[pos-1]], point[p[pos]]));
     used[i]--;
            }
    }

    []对于01的所有排列,从中同时选取r个元素使得01的数量不同。

    这道题很简单,其实就是从02^r的二元表示。

    算法如下:

    void dfs(int pos)
    {
       if (pos == r)
       {
           for (i=0; i<r; i++) cout<<p[i];
           cout<<endl;
           return;
       }
       p[pos] = 0;
       dfs(pos+1);
       p[pos] = 1;
       dfs(pos+1);}

    相关问题:

    Ural

    1005 Stone pile

    1060 Flip Game

    1152 The False Mirrors

     

    []找最大团问题。

    一个图的团,就是包括了图的所有点的子图,并且是连通的。也就是说,一个子图包含了n个顶点和n*(n-1)/2条边,找最大团问题是一个NP问题。算法如下:

    #define MaxN 50
     
    int  n, max;
    int  path[MaxN][MaxN];
    int  inClique[MaxN];
     
    void dfs(int inGraph[])
    {
    int i, j;
    int Graph[MaxN];
     
    if ( inClique[0]+inGraph[0]<=max ) return;
    if ( inClique[0]>max ) max=inClique[0];
     
    /*对于图中的所有点*/
        for (i=1; i<=inGraph[0]; i++)
        {
    /*把节点放置到团中*/
            ++inClique[0];
     inClique[inClique[0]]=inGraph[i];
    /*生成一个新的子图*/
     Graph[0]=0;
     for (j=i+1; j<=inGraph[0]; j++)
         if (path[inGraph[i]][inGraph[j]] )
              Graph[++Graph[0]]=inGraph[j];
         dfs(Graph);
    /*从团中删除节点*/
            --inClique[0];}
    }
    int main()
    {
    int inGraph[MaxN];
    int i, j;
      cin >>n;
      while (n > 0)
      {
            for (i=0; i<n; i++)
     for (j=0; j<n; j++)
         cin >>path[i][j];
            max = 1;
    /*初始化*/
            inClique[0]= 0;
            inGraph[0] = n;
     for (i=0; i<n; i++) inGraph[i+1]=i;
            dfs(inGraph);
            cout<<max<<endl;
            cin >>n;
      }
      return 0;}

  • 相关阅读:
    动态修改类注解(赋值)
    Javassist字节码增强示例
    修改原有的方法名称(字节码增强)
    mock测试之powermock
    SpringMVC防止表单重复提交
    Node.js中的异步I/O是如何进行的?
    移动端三个视口
    javascript 正则(将数字转化为三位分隔的样式)
    Struts2中数据封装方式
    Struts2配置文件
  • 原文地址:https://www.cnblogs.com/kingeric/p/902100.html
Copyright © 2011-2022 走看看