题目大意
仙女星幼儿园的快班小朋友们下周要到粟洞公园郊游。带队教师想在郊游时让两名学生组成一个小队进行活动。不过让两名不是朋友的学生组成一队会发生争执或者互不理睬。因此,必须由两名朋友关系的学生组队。
给定各学生的朋友关系详情,编写程序计算出所有可配对的不同方法。任何一个不相同的配对都将被视为一种不同的配对方法。例如,以下两种配对方法就属于不同的配对方法。
●(泰妍,杰西卡)(珊妮,蒂芬妮)(孝渊,俞利)
●(泰妍,杰西卡)(珊妮,俞利)(孝渊,蒂芬妮)
解题思路:
为了使用递归调用,首先将整个操作过程分解为几个小的操作。这道题中,将整个问题分解为m/2个小操作,每个小操作等同于对两名学生的组队。此时问题将变为“给定还未组队的学生名单时,计算出两名朋友间组成小队的组合个数”。因为名单中找出两名互为朋友关系的学生组队后,对剩下学生的组队又相当于前一步骤的操作过程。
出现的问题:
计算组合个数时会经常遇到重复相同答案的现象。为了解决这种错误,可采取只计算特定形态答案的方法。通常使用的方法是,在相同答案中以字典顺序只选择最先得到的答案。例如,忽略(2,3)、(0,1)或(1,0)、(2,3),而选择(0,1)、(2,3)。
为了强制这种属性,在各操作阶段只为序号最靠前的学生配上伙伴,即可解决前面提到的两种问题。对序号最靠前的学生而言,其伙伴的序号肯定比其靠后,所以不可能得到像(1,0)这一类的答案。又因为总是从序号最靠前的学生开始配对,所以也不可能出现像(2,3)、(0,1)这一类答案。代码6-5是按照这种思路编写的代码。
int n ;
bool areFriend[10][10];
// 若第take[i] = i 个学生找到了伙伴,则返回true,否则为false
int countPairings(bool take[10]){
int firstFree = -1;
for(int i =0;i<n;++i){
if(!take[i]){
firstFree = i;
break;
}
}
//所有的学生都匹配上了
if(firstFree == -1)return 1;
int ret = 0;
//选择与学生组队的伙伴
for(int pairWith = firstFree+1;pairWith<n;++i){
if(!take[pairWith]&&areFriend[firstFree][pairWith]){
take[firstFree] = take[pairWith] = true;
ret += countPairings(take);
take[firstFree] = take[pairWith] = false;
}
}
return ret;
}