acm搜索算法dfs排列组合.docx
- 文档编号:30664521
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:39
- 大小:33.29KB
acm搜索算法dfs排列组合.docx
《acm搜索算法dfs排列组合.docx》由会员分享,可在线阅读,更多相关《acm搜索算法dfs排列组合.docx(39页珍藏版)》请在冰豆网上搜索。
acm搜索算法dfs排列组合
排列与组合(非递归的DFS算法)
组合的生成与排列相似,组合中数字相同顺序不同的可以归纳为一个组合,可以按升序生成或降序生成,即后面搜索到的数要大于(或小于)前面的数,我在这里以升序为例。
#include
voidcomb(intm,intn)
{
intt,k,i,a[20];
k=1;
t=0;
a[0]=0;
a[k]=0;
while(k>0){
a[k]++;//向下搜索
while((a[k]<=m)&&(a[k]<=a[k-1]))a[k]++;//判断当前结点是不是可行的,若不是则在容许范围内继续搜索
if(a[k]<=m){//有没有超出范围
if(k==n){//是不是目标解
for(i=1;i<=n;i++)
printf("%d",a[i]);
t++;//输出完毕回溯到上一层,继续搜索其他解
k--;
printf("\n");
}
else{//不是则进行下一层的搜索
k++;
a[k]=0;
}
}
elsek--;//若当前状态找不到可行的解则回溯
}
printf("sum:
%d",t);
}
intmain()
{
intm,n;
scanf("%d%d",&m,&n);
comb(m,n);
return0;
}
#include
usingnamespacestd;
intn[1001],vtd[1001],digits,nums;
voiddfs(intx,intcrnt);
intmain()
{
memset(n,0,sizeofn);
memset(vtd,0,sizeofvtd);
cin>>nums>>digits;
dfs(1,1);
return0;
}
voiddfs(intx,intcrnt)
{
if(x<=digits)
{
for(intj=crnt;j<=nums;j++)
if(!
vtd[j])
{
vtd[j]=1;
n[x]=j;
dfs(x+1,j);
vtd[j]=0;cout< cout< } } #include usingnamespacestd; intn,m; intvst[11]; intp[11]; void __read__() { cin>>n>>m; } void __outp__() { for(inti=1;i<=m;i++) cout< cout< } void __dfs__(intx) { if(x<=m) for(inti=1;i<=n;i++) if(! vst[i]) { vst[i]=true; p[x]=i; __dfs__(x+1); vst[i]=false; } if(x>m) __outp__(); } int main() { __read__(); __dfs__ (1); return0; } [前言]这篇论文主要针对排列组合对回溯算法展开讨论,在每一个讨论之后,还有相关的推荐题。 在开始之前,我们先应该看一下回溯算法的概念,所谓回溯: 就是搜索一棵状态树的过程,这个过程类似于图的深度优先搜索(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} 算法如下: intn,r; charused[MaxN]; intp[MaxN]; voidpermute(intpos) {inti; /*如果已是第r个元素了,则可打印r个元素的排列*/ if(pos==r){ for(i=0;i cout<<(p[i]+1); cout< return; } for(i=0;i if(! used[i]){/*如果第i个元素未用过*/ /*使用第i个元素,作上已用标记,目的是使以后该元素不可用*/ used[i]++; /*保存当前搜索到的第i个元素*/ p[pos]=i; /*递归搜索*/ permute(pos+1); /*恢复递归前的值,目的是使以后改元素可用*/ used[i]--; } } 相关问题 UVA524PrimeRingProblem 可重排列: 就是从任意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}. 算法如下: #defineFREE-1 intn,r; /*使元素有序*/ intE[MaxN]={0,0,1,1,1}; intP[MaxN]; charused[MaxN]; voidpermute(intpos) { inti; /*如果已选了r个元素了,则打印它们*/ if(pos==r){ for(i=0;i cout< cout< return; } /*标记下我们排列中的以前的元素表明是不存在的*/ P[pos]=FREE; for(i=0;i /*如果第I个元素没有用过,并且与先前的不同*/ if(! used[i]&&E[i]! =P[pos]){ /*使用这个元素*/ used[i]++; /*选择现在元素的位置*/ P[pos]=E[i]; /*递归搜索*/ permute(pos+1); /*恢复递归前的值*/ used[i]--; } } 相关习题 UVA10098GeneratingFast,SortedPermutations 组合: 从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}. 算法如下: intn,r; intC[5]; charused[5]; voidcombine(intpos,inth) { inti; /*如果已选了r个元素了,则打印它们*/ if(pos==r){ for(i=0;i cout< cout< 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]--; } } 相关问题: Ural1034Queensinpeacefulposition 可重组合: 类似于可重排列。 [例]给出空间中给定n(n<10)个点,画一条简单路径,包括所有的点,使得路径最短。 解: 这是一个旅行售货员问题TSP。 这是一个NP问题,其实就是一个排列选取问题。 算法如下: intn,r; charused[MaxN]; intp[MaxN]; doublemin; voidpermute(intpos,doubledist) { inti; if(pos==n){ if(dist return; } for(i=0;i if(! used[i]){ used[i]++; p[pos]=i; if(dist+cost(point[p[pos-1]],point[p[pos]]) permute(pos+1,dist+cost(point[p[pos-1]],point[p[pos]])); used[i]--; } } [例]对于0和1的所有排列,从中同时选取r个元素使得0和1的数量不同。 解这道题很简单,其实就是从0到2^r的二元表示。 算法如下: voiddfs(intpos) { if(pos==r) { for(i=0;i cout< return; } p[pos]=0; dfs(pos+1); p[pos]=1; dfs(pos+1);} 相关问题: Ural 1005Stonepile 1060FlipGame 1152TheFalseMirrors [例]找最大团问题。 一个图的团,就是包括了图的所有点的子图,并且是连通的。 也就是说,一个子图包含了n个顶点和n*(n-1)/2条边,找最大团问题是一个NP问题。 算法如下: #defineMaxN50 intn,max; intpath[MaxN][MaxN]; intinClique[MaxN]; voiddfs(intinGraph[]) { inti,j; intGraph[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];} } intmain() { intinGraph[MaxN]; inti,j; cin>>n; while(n>0) { for(i=0;i for(j=0;j cin>>path[i][j]; max=1; /*初始化*/ inClique[0]=0; inGraph[0]=n; for(i=0;i dfs(inGraph); cout< cin>>n; } return0;} 尽管排列组合是生活中经常遇到的问题,可在程序设计时,不深入思考或者经验不足都让人无从下手。 由于排列组合问题总是先取组合再排列,并且单纯的排列问题相对简单,所以本文仅对组合问题的实现进行详细讨论。 以在n个数中选取m(0 1.首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,直到从n-(m-1)个数中选取1个数为止。 2.从n个数中选取编号次小的一个数,继续执行1步,直到当前可选编号最大的数为m。 很明显,上述方法是一个递归的过程,也就是说用递归的方法可以很干净利索地求得所有组合。 下面是递归方法的实现: ///求从数组a[1..n]中任选m个元素的所有组合。 ///a[1..n]表示候选集,n为候选集大小,n>=m>0。 ///b[1..M]用来存储当前组合中的元素(这里存储的是元素下标), ///常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。 voidcombine(inta[],intn,intm,intb[],constintM) { for(inti=n;i>=m;i--)//注意这里的循环范围 { b[m-1]=i-1; if(m>1) combine(a,i-1,m-1,b,M); else//m==1,输出一个组合 { for(intj=M-1;j>=0;j--)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- acm 搜索 算法 dfs 排列组合