排列组合与回溯算法

 

原创 排列组合与回溯算法

<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>

排列组合与回溯算法

KuiBing

感谢BambooLeeMaRS的帮助

[关键字] 递归 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;}

 

 参考论文 <A fast algorithm for the maximum clique problem>

相关问题:

acm.zju.edu.cn: 1492 maximum clique

 

相关网站

http://acm.uva.es/p

http://acm.timus.ru/

 

Contact me:

MSN: Bing0672@Hotmail.com


原贴地址:http://blog.csdn.net/kbsoft/archive/2003/11/13/10294.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值