全排列问题集锦

这类题目属于搜索类问题,一般可采用搜索,进行暴力求解。

1 生成1~n的全排列

问题重述

输入一个自然数N(1<=N<=9),从小到大输出用1~N组成的所有排列,也就说全排列。
##代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n;
int visited[10]; //标记已使用过的数字
int A[10]; //存放排列好的数
int ans; //记录全排列的种数
void print_permutation(int cur)
{
	if(cur==n) //边界条件
	{
		ans++;
		for(int i=0;i<n;i++)
		{
			printf("%d",A[i]);
		}
		printf("\n");
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(!visited[i]) //如果标记为0,则该数字未使用过
		{
			A[cur]=i;  
			visited[i]=1;  //该数字已使用,标记为1
			print_permutation(cur+1);
			visited[i]=0; //很关键,一定要再次标记为0,为了进行下一次尝试
		}
	}

}
int main()
{
	cin>>n;
    print_permutation(0);
	printf("%d\n",ans);
	return 0;
}

2 生成无重集的排列

问题描述

输入N个自然数(1<=N<=9),从小到大输出用N个自然数组成的所有排列。例如:输入3个自然数2 6 9,输出
269
296
629
692
926
962

基于上面题解法,只需稍微改下,定义一个数组,用来存放N个自然数。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n;
int visited[10];
int A[10],As[10];
int ans;
void print_permutation(int cur)
{
	if(cur==n)
	{
		ans++;
		for(int i=0;i<n;i++)
		{
			printf("%d",A[i]);
		}
		printf("\n");
		return;
	}
	for(int i=0;i<n;i++)
	{
		if(!visited[As[i]])
		{
			A[cur]=As[i];
			visited[As[i]]=1;
			print_permutation(cur+1);
			visited[As[i]]=0;
		}
	}

}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>As[i];
	}
    print_permutation(0);
	printf("%d\n",ans);
	return 0;
}

生成可重集的排列

问题描述

以上两种题都是对无重复元素的排列,但有时遇到有重复元素时该如何排列呢,比如对1 1 1全排列。

代码

#include<stdio.h>
int k=0;
void print_permutation(int n,int* p,int* A,int cur)
{
	
	if(cur==n)//递归边界 
	{
		++k;
		for(int i=0;i<n;i++)
		{
			printf("%d",A[i]);
		}
		printf("\n");
	}
	else //尝试在A[cur]中填各种i 
	{
		for(int i=0;i<n;i++)
		{
			if(!i||p[i]!=p[i-1]) 
			{
			int c1=0,c2=0;
			for(int j=0;j<cur;j++) if(A[j]==p[i]) c1++;
			for(int j=0;j<n;j++)
			{
				if(p[j]==p[i]) 
				c2++;//如果i已经在A【0】~A【cur-1】中出现,则不再选 
			}
			if(c1<c2)
			{
				A[cur]=p[i];
				print_permutation(n,p,A,cur+1);//递归调用 
			}
			}
		}
	}
}
int main()
{
	int n,A[10000],cur=0,p[10000];
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&p[i]);
	}
	print_permutation(n,p,A,cur);
	printf("%d\n",k);
	return 0;
}

4 NYOJ 擅长排列的小明

该题是南阳oj上的一道搜索题,这道题只需要一个限制变量即可,解法跟第一道题相同

问题描述

小明十分聪明,而且十分擅长排列计算。比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想为难他,在这5个数字中选出几个数字让他继续全排列,那么你就错了,他同样的很擅长。现在需要你写一个程序来验证擅长排列的小明到底对不对。

输入

第一行输入整数N(1<N<10)

输出

在1-n中选取m个字符进行全排列,按字典序全部输出,每种排列占一行,每组数据间不需分界。

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m;
int cur=0,ans[10],book[10];
void dfs(int cur)
{
    if(cur==m)
    {
        for(int i=0;i<m;i++)
        {
            printf("%d",ans[i]);
        }
        printf("\n");
        return;
    }
    for(int i=1;i<=n;i++)
    {
        
        if(!book[i])
        {
            ans[cur]=i;
            book[i]=1;
            dfs(cur+1);
            book[i]=0;
        }
        
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        dfs(0);
    }
    return 0;
}

组合数

题目描述

找出从自然数1、2、… 、n(0< n<10)中任取r(0< r<=n)个数的所有组合。

输入

输入n,r

输出

按特定顺序输出所有组合。
特定顺序:每一个组合中的值从大到小排列,组合之间按逆字典序排列。

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m;
int cur=0,ans[10];
void dfs(int cur,int h)
{
    if(cur==m)
    {
        for(int i=0;i<m;i++)
        {
            printf("%d",ans[i]);
        }
        printf("\n");
        return;
    }
    for(int i=h;i>0;i--)
    {
        ans[cur]=i;
        dfs(cur+1,i-1);
    }
}
int main()
{
        cin>>n>>m;
        dfs(0,n);
        return 0;
}

K进制数

##问题描述
考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0.
考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0.
给定两个数N和K, 要求计算包含N位数字的有效K-进制数的总数.(2 <= K <= 10; 2 <= N; 4 <= N+K <= 18.)
例如:
1010230 是有效的7位4进制数
注意:0001235 不是7位数, 而是4位数.

输入

两个十进制整数N和K。

输出

一个十进制正整数,为包含N位数字的有效K-进制数的总数。

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int cs[10];
int n=0,k=0,ans=0;
void dfs(int cur)
{
    if(cur==n)
    {
        ans++;
        return;
    }
    for(int i=0;i<k;i++)
    {
        if((cur==0&&i==0)||(cur>0&&cs[cur-1]==0&&i==0)) continue; //这一步是关键,首先保证首位不能是0,并且相邻两位不能同是0
        cs[cur]=i;
        dfs(cur+1);
    }
}
int main()
{
    cin>>n>>k;
    dfs(0);
    cout<<ans<<endl;
    return 0;
}

下一个排列函数next_permutation

c++的STL中提供了一个库函数next_permutation,该函数是“求下一个排列”。那该函数该如何使用呢,还是上代码来解释吧!

#include<cstdio>
#include<iostream>
#include<algorithm> //包含next_permutation,sort
using namespace std;
int main()
{
	int n,p[10];
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		cin>>p[i];
	sort(p,p+n); //使用该函数进行全排列条件,数组元素必须是从小到大排列
	do
	{
		for(int i=0;i<n;i++)
		{
			printf("%d ",p[i]);
		}
		printf("\n");
	} while (next_permutation(p,p+n)); //每次调用该函数,产生下一个排列
	return 0;
}

上一个排列函数prev_permutation

类似的,有下一个排列函数,就有上一个排列函数,该函数与下一个排列函数使用方法相同,但该函数全排列前提条件自然就是数组元素从大到小排列了。就不展示该函数的代码了。

很好用的两个排列函数,学会了可以节省大量时间来编写排列递归函数,哈哈哈!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值