题目:
1~n之内的数字排列,可以全排,或者抽其中m个数排列,带顺序或不带顺序,分别用递归和回溯法编写
(其实全部排列和只排其中的m(m<=n)个数差不多,就是在判别当前位置是否达到n或m,只用改一行代码就行了。重点是这个顺序问题)
一.: 1~n之内的排列,带顺序! A(m,n) 【递归】
分析:这个算法其实很常见,做过几道算法题的大家都懂,每个位置上1~n数全部试一次,然后再判断当前位置上的数跟前面位置的数比较有没有重复,有就换下一个数,没有就递归下一个位置。
//1~n之间抽取n个数进行排列【递归】
#include<stdio.h>
#define MAXN 1000
int a[MAXN];
int n,m; //n是 几个数进行排列,m是指1~n之间抽取m个数进行排列
int count=0 ; //共有count种排列方法
void f(int cur)
{
int i,j;
//我的数组是从1开始赋值的,若是0开始,则cur==m就行了
if(cur>m) //注意,在区间取m个数或者全排列的区别就在这里,全排列则改为cur>n
{
count++;
return ;
}
for(i=1;i<=n;i++)
{
int ok=1;
for(j=1;j<cur;j++) //将当前位置上的数和 前面位置的数进行比较,有重复的话换一个值去赋值
if(a[j]==i)
{
ok=0;
break;
}
if(ok)
{
a[cur]=i;
f(cur+1);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
f(1);
printf("%d\n",count);
return 0;
}
二. 1~n之内的排列,不在乎顺序!C(m,n) 【递归】
分析:注意到组合与组成顺序无关,约定组合中的组成元素按递增排序。所以把上面代码排序的约束条件修改 :
for(i=a[cur-1]+1;i<n-m+cur;i++)
a[cur]=i;
循环起点为a[cur-1]+1,即a[cur]的值比a[cur-1]大,避免了元素取相同值的判别!
循环终点为n+cur-m,即a[cur]最大只能取n+cur-m,为后面m-cur个元素a[cur+1],…….a[m]留下取值空间(后面的元素取值比a[cur]大,且最大取到n)
注意:上个程序数组下标直接从1开始赋值,在它的基础上修改为这个函数,优势就出来了!!!
//1~n之间抽取n个数进行排列【递归】
#include<stdio.h>
#define MAXN 1000
int a[MAXN];
int n,m; //n是 几个数进行排列,m是指1~n之间抽取m个数进行排列
int count=0 ; //共有count种排列方法
void f(int cur)
{
int i,j;
//我的数组是从1开始赋值的,若是0开始,则cur==m就行了
if(cur>m) //注意,在区间取m个数或者全排列的区别就在这里,全排列则改为cur>n
{
count++;
return ;
}
a[0]=0;
for(i=a[cur-1]+1;i<=n-m+cur;i++) //无需再判断是否有重复值,这是递增的,不存在
{
a[cur]=i;
f(cur+1);
}
}
int main()
{
scanf("%d%d",&n,&m);
f(1);
printf("%d\n",count);
return 0;
}
这个这个要开始讨论回溯了,先考虑1~n的全排列:
三 . 1~n之内的排列,带顺序! A(n,n) 【回溯】
分析:首先从a[1]=1开始,逐步给a[cur](1<=cur<=n)赋值,每一个a[cur]赋值从一开始递增到n,直至a[n]赋值,判断:
1。若cur=n,且ok=1即无重复,则为一组解
2。若cur《n且ok=1,表明还不到9个数字,则下一个a[i]从1开始赋值继续
3.。若a[n]=n,则返回前一个数组元素,a[n-1]增1赋值(a[n]从1开始再一个个值的试),直到a[1]=9为止,已无法返回,意味全部测试完,可结束
#include<stdio.h>
int main()
{
int a[100];
int n,i,count=0,ok,cur;
scanf("%d",&n);
a[cur=1]=1;
while(1)
{
ok=1;
for(i=1;i<cur;i++) //判断当前位置与之前的位置上的数是否有重复
if(a[i]==a[cur])
{
ok=0;
break;
}
if(cur==n && ok)
count++; //种类数
if(cur<n && ok) //不到n个数,往后赋值,接着赋值
{
cur++;
a[cur]=1;
continue;
}
while(a[cur]==n && cur>1) //往前回溯
cur--;
if(a[cur]==n && cur==1) //结束标志
break;
else
a[cur]++;
}
printf("%d\n",count);
return 0;
}
四。 1~n之内的排列,不在乎顺序!C(m,n) 【回溯】
分析:首先起点为a[cur-1]+1,即a[cur]的值比a[cur-1]大,逐步给a[cur](1<=cur<=n)赋值,直至a[m]赋值,判断:
1。若cur《m,表明还不到m个数字,则下一个a[cur]继续从a[cur-1]+1开始赋值
2。若cur=m,则为一组解
3.。若cur>1且a[cur]=n+cur-m时,即该位置的数已取到最大值,往前回溯
4..。若cur=1且a[cur]=n-cur+m,即所以可能已取完,退出循环,否则该位置的数加1,继续判断
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a[100];
int n,m,count=0,cur;
scanf("%d%d",&n,&m); //n个数中选取m个数进行无顺序排列,要是在n个数中选n个数无顺序排列就没啥意思,答案就是1
a[0]=0;cur=0;
while(1)
{
if(cur<m) //不到m个数,往后赋值,接着赋值
{
cur++;
a[cur]=a[cur-1]+1;
continue;
}
if(cur==m)
{count++; //种类数
for(int i=1;i<=cur;i++)
printf("%d",a[i]);
printf("\n");system("PAUSE");
}
while(a[cur]==n+cur-m && cur>1) //往前回溯
cur--;
if(cur==1 && a[cur]==n+cur-m) //结束标志 :递增排列的,a[1]最多取到n+cur-m,还要给剩下的m-cur位数赋值
break;
else
a[cur]++;
}
printf("%d\n",count);
return 0;
}