写在前面
这个题目是我在做公司招聘笔试题遇到的,当时想用排列组合的方法和公式来做,然而根本没有思路,最后也没有做出来。白白花了不少时间。交卷后又一直在想到底怎么解决,还是想到了一种解决的办法。
问题描述
在游戏Dota2中,某英雄有三种属性:冰、火、雷。同时身上有三个无顺序的属性槽,他可以从三种属性中任意选择三个放入属性槽中,然后通过当前的属性组合召唤技能。每种不同的属性组合都可以召唤出不同的技能,属性的组合是无序的,即冰火雷,冰雷火,火雷冰,火冰雷,雷火冰,雷冰火只算一种组合,因此共有十种组合:
1、冰冰冰
2、冰冰火
3、冰冰雷
4、冰火火
5、冰火雷
6、冰雷雷
7、火火火
8、火火雷
9、火雷雷
10、雷雷雷
现在有一个英雄拥有n种属性,同时身上有m个无顺序的属性槽,则问该英雄可以召唤出多少种不同的技能?
要求输入n和m,输出组合出不同技能的个数
例:
输入:
3 3
输出:
10
解决思路
如果将冰,火,雷依次用数字1,2,3来表示
那么题中组合结果为:
111,112,113,122,123,133,222,223,233,333
将一种组合看做一个整数,通过观察发现这些数的百位<=十位<=各位,并且10组数是从小到大依次排列
另外,112,121,211都是一种组合,123,132,213,231,312,321也都算一种组合。
那么现在自己人为规定组合是有顺序的,将属性用不同的数字表示,那么组合后的第一个数<=第二个数<=第三个数<=···<=第m个数。
这样列出的组合肯定是没有重复的。
接下来就是如何用代码去求组合的个数。
我的想法是用递归来实现,将一个大的问题分成若干个同样的小问题。
首先,m个槽可以分为第一个槽+后面m-1个槽,先确定第一个槽中放什么属性,再确定后面的槽放什么属性。这样只要再处理后面m-1的槽,依次减小要处理的槽的数量。
其次,后一个槽中属性对应的数值必须>=前一个槽中属性对应的数值。有了相应的关系才能方便递归。
最后递归的终止条件就是只剩一个空槽,根据前一个槽中属性的数组,确定最后一个槽中能有几种放法。
递归是需要输入的参数有前一个槽中的属性值,当前还剩的空槽的数值以及属性的最大值。
C代码
#include <stdio.h>
/*递归函数
*输入参数:
*i:前一个槽中所放属性值
*m:当前还剩的空槽的个数
*n:属性的最大值
*返回值:当前i和m条件下的组合个数
*/
int process(int i, int m, int n)
{
if (m == 1)
{
return n - i + 1;//递归结束条件
}
else
{
int k;//当前第一个槽中放的值
int sum = 0;//用于求和
//由于第一个槽中可以放的属性值可以有若干个不同的选择,且属性值要>=前一个槽中的属性值,因此用一个循环来累加
for (k = i; k <= n; k++)
{
sum += process(k, m - 1, n);
}
return sum;
}
}
int main()
{
int m,n,result;
scanf("%d %d",&m,&n);//输入槽的个数与属性的个数
result=process(1,m,n);
//计算组合的个数,这里的1是因为第一个槽属性值从1开始,那么假设有前一个槽,那槽中的值为1。
printf("%d\n",result);//打印输出
return 0;
}
说在最后
关于代码的正确性,我选了n和m不大的几种情况来进行验证,输出结果正确。若有错误,欢迎只出。