排列组合是组合学最基本的概念。所谓排列,就是指从给定个数的元素中取出指定个数的元素进行排序。组合则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。
排列与组合在日常生活中应用较广,比如在考虑某些事物在某种情况下出现的次数时,往往需要用到排列和组合。
【例1】n位二进制数的全排列。
编写一个程序,输入一个自然数n(1<=n<=10),输出n位二进制数的全排列。例如,输入3,输出8个3位二进制数的序列:000、001、010、011、100、101、110、111。
(1)编程思路。
可以采用递归的思想来解决这个问题。
设将产生n位二进制数的全排列的解决方法记为P(n),显然,当n=1时,该位或置1或置0,问题解决;当n>1时,可以考虑将最高位或最低位置1或0,再按同样的方法产生其余n-1位二进制数的全排列,即递归调用P(n-1)。
具体做法是:
用一维数组a来保存产生的n位二进制数序列,产生过程是从最低位(第0位)到最高位(第n-1位)逐位产生的,每位可以有0和1两种情况,设产生n位二进制数中的第k位~第n-1位的操作记为p(int *a,int k,int n),则这一操作过程为:
if (k==n) // 第0位到第n-1位共n位全部产生
{
输出结果;
return; // 递归终止
}
a[k]=0; // 当前第k位置0
p(a,k+1,n); // 产生第k+1 位~第n-1位
a[k]=1; // 当前第k位置 1
p(a,k+1,n); // 产生第k+1位~第n-1位
(2)源程序。
#include <iostream>
using namespace std;
void p(int *a,int k,int n)
{
if (k==n)
{
for (int i=0;i<n;i++)
cout<<a[i];
cout<<endl;
return;
}
a[k]=0;
p(a,k+1,n);
a[k]=1;
p(a,k+1,n);
}
int main()
{
int n,a[10];
cin>>n;
p(a,0,n);
return 0;
}
上面的例子产生了n位二进制数序列,若每位对应一个元素,0代表该元素不出现,1代表该元素出现;或每位对应一个问题,0代表该问题没有解决,1代表该问题被正确解决,这样可以在此基础上,解决实际的问题。
【例2】低碳生活大奖赛 (2012年第3届蓝桥杯省赛试题)。
某电视台举办了低碳生活大奖赛。题目的计分规则相当奇怪:
每位选手需要回答10个问题(其编号为1到10),越后面越有难度。答对的,当前分数翻倍;答错了则扣掉与题号相同的分数(选手必须回答问题,不回答按错误处理)。
每位选手都有一个起步的分数为10分。
某获胜选手最终得分刚好是100分,如果不让你看比赛过程,你能推断出他(她)哪个题目答对了,哪个题目答错了吗?
如果把答对的记为1,答错的记为0,则10个题目的回答情况可以用仅含有1和0的串来表示。例如:0010110011 就是可能的情况。
编写程序,输出所有可能的表示回答情况的字符串。
(1)编程思路。
采用例1的思路产生10位二进制数的全排列,每位二进制数码分别对应一个问题的解答情况,0代表答错,1代表回答正确。若计算出的分数恰好为100,则就是问题的一组解。
(2)源程序。
#include <iostream>
using namespace std;
void f(int s[] , int n , int score)
{
int i;
if(n>=11)
{
if(score == 100)
{
for(i=1;i<=10;i++)
cout<<s[i];
cout<<endl;
}
return;
}
s[n]=0; // 第n题答错
f(s, n+1, score-n);
s[n]=1; // 第n题答对
f(s, n+1, score*2);
}
int main()
{ int s[11];
s[10] = ' ' ;
f(s,1,10);
return 0;
}
【例3】李白打酒 (2014年第5届蓝桥杯省赛试题)。
大诗人李白,一生好饮。一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。请输出所有像这样的答案。
(1)编程思路。
同样按例1的方法生成15位二进制数的排列,对每一种排列进行判断,看是否满足要求(遇到店5次、酒正好喝完、最后一次遇到的是花),满足条件输出对应字符串即可。
(2)源程序。
#include <iostream>
using namespace std;
void dfs(int *a,int k)
{
if (k==15)
{
int alcohol,drinkery,i;
alcohol=2; drinkery=0;
for (i=0;i<15;i++)
if (a[i]==0)
{
alcohol=2*alcohol;
drinkery++;
}
else
alcohol--;
if (alcohol==0 && drinkery==5 && a[14]==1)
{
for (i=0;i<15;i++)
if (a[i]==0) cout<<"a";
else cout<<"b";
cout<<endl;
}
return;
}
a[k]=0;
dfs(a,k+1);
a[k]=1;
dfs(a,k+1);
}
int main()
{
int a[15];
dfs(a,0);
return 0;
}
(3)程序优化。
上面的程序中用二进制数0表示遇到店,1表示遇到花。实际上,可以直接定义一个字符数组char a[16],该数组的元素a[0]~a[14]直接用字符’a’或’b’ 赋值,a[15]赋结束符’