阿里笔试分丸子问题

  • 题目
  • 解题思路
  • 完整代码实现

题目

有鱼丸M个,肉丸N个,碗K个。现将鱼丸和肉丸分放到这些碗里,碗可以是空的(什么也不放)。每个碗可以装的丸子数量不限,所有的碗是一样的,同一个碗里不能既有鱼丸又有肉丸。求有多少种放法。这里1<=M,N,K<=50。
例如,当N=M=1,K=3时,只有一种放法,因为(1,1,0), (1,0,1), (0,1,1)视为同一种。最后输出的结果要求取10000的余数,如结果为123456,则只需输出3456。
注:题目是博主回忆的,表达上与原题可能略有不同,但应该没有歧义。

题目提供了代码模板:

/** 请完成下面这个函数,实现题目要求的功能 **/
/** 当然,你也可以不按照这个模板来作答,完全按照自己的想法来 ^-^  **/
int ballAllocate(int m, int n, int k) {
}

int main() {
	int res;

	int _m;
	cin >> _m;
	cin.ignore(std::numeric_limits::max(), '\n');


	int _n;
	cin >> _n;
	cin.ignore(std::numeric_limits::max(), '\n');


	int _k;
	cin >> _k;
	cin.ignore(std::numeric_limits::max(), '\n');



	res = ballAllocate(_m, _n, _k);
	cout << res << endl;

	return 0;
}

解题思路

这里提供博主的解题思路,仅供参考,如有错误欢迎大家指出。如有更好的方法也希望有好心人能告知博主。

首先分析题目意思可以知道,这个问题其实鱼丸和肉丸是独立的。分鱼丸不影响肉丸,分肉丸不影响鱼丸。所以总的方法数应当由分鱼丸的方法数乘以分肉丸的方法数得到。考虑到这里的碗是一样的,我们只需要求一个有序的序列,比如3个鱼丸2个碗,只需要求出(1,2),不必再考虑(2,1)的情况。
所以问题转化为求一种丸子的有序分配方法数。假设结果按丸子数不减的序列排列,实现一个非空的丸子分配函数:

int ballAllocateNotEmpty(int n, int k, int upBound) {
	int result = 0;
	if (k == 1) {//只有一个碗
		if (n <= upBound) {//丸子数不超过上限
			result = 1;//有一种分法
		} else {//丸子数超过上限,不满足有序序列
			result = 0;//该分法不成立
		}
	} else {//有多个碗
		if (n < k) {//丸子数小于碗数,必出现空碗
			result = 0;//该分法不满足
		} else if (n == k) {//丸子数等于碗数
			result = 1;//只有一种分法,每个碗里放1个
		} else {
			for (int i = upBound; i > 0; i--) {//取一个碗,碗里可能放[1,upBound]个丸子
				//当前碗里放i个丸子,剩余k-1个碗分配剩下的n-i个丸子,每个碗里丸子上限i个
				result += ballAllocateNotEmpty(n - i, k - 1, i);
			}
		}
	}
	return result;
}

其中n表示n个丸子,k表示k个碗,upBound表示一个碗里的丸子数量上限,返回有几种分法,不出现空碗。

有了单独的分丸子函数后,再分配鱼丸和肉丸就只简单了:

int ball2AllocateNotEmpty(int m, int n, int k) {
	int result = 0;
	for (int i = 1; i < k; i++) {
		int result_yuwan = ballAllocateNotEmpty(m, i, m);//i个碗放鱼丸
		int result_rouwan = ballAllocateNotEmpty(n, k - i, n);//k-i个碗放肉丸
		result += result_yuwan * result_rouwan;
	}
	return result;
}

m表示鱼丸数,n表示肉丸数,k表示碗数。按照(鱼丸,肉丸)分别分配到(1,k-1),(2,k-2)…(k-1,1)个碗中的方法数求和即得分配两种丸子的方法数(这里同样不出现空碗)。

最后,我们求整个分配过程的总方法数(包括出现空碗的情况)。按照空碗的数量分别对方法数进行求和:

int ballAllocate(int m, int n, int k) {
	int total = 0;
	for (int i = 0; i < k; i++) {
		total += ball2AllocateNotEmpty(m, n, k - i);//i个空碗,k-i个非空碗
	}
	return total % 10000;//结果按要求取余
}

完整代码实现

#include 

using namespace std;

int ballAllocateNotEmpty(int n, int k, int upBound) {
	int result = 0;
	if (k == 1) {//只有一个碗
		if (n <= upBound) {//丸子数不超过上限
			result = 1;//有一种分法
		} else {//丸子数超过上限,不满足有序序列
			result = 0;//该分法不成立
		}
	} else {//有多个碗
		if (n < k) {//丸子数小于碗数,必出现空碗
			result = 0;//该分法不满足
		} else if (n == k) {//丸子数等于碗数
			result = 1;//只有一种分法,每个碗里放1个
		} else {
			for (int i = upBound; i > 0; i--) {//取一个碗,碗里可能放[1,upBound]个丸子
											   //当前碗里放i个丸子,剩余k-1个碗分配剩下的n-i个丸子,每个碗里丸子上限i个
				result += ballAllocateNotEmpty(n - i, k - 1, i);
			}
		}
	}
	return result;
}

int ball2AllocateNotEmpty(int m, int n, int k) {
	int result = 0;
	for (int i = 1; i < k; i++) {
		int result_yuwan = ballAllocateNotEmpty(m, i, m);//i个碗放鱼丸
		int result_rouwan = ballAllocateNotEmpty(n, k - i, n);//k-i个碗放肉丸
		result += result_yuwan * result_rouwan;
	}
	return result;
}

/** 请完成下面这个函数,实现题目要求的功能 **/
/** 当然,你也可以不按照这个模板来作答,完全按照自己的想法来 ^-^  **/
int ballAllocate(int m, int n, int k) {
	int total = 0;
	for (int i = 0; i < k; i++) {
		total += ball2AllocateNotEmpty(m, n, k - i);//i个空碗,k-i个非空碗
	}
	return total % 10000;//结果按要求取余
}

int main() {
	int res;

	int _m;
	cin >> _m;
	cin.ignore(std::numeric_limits::max(), '\n');


	int _n;
	cin >> _n;
	cin.ignore(std::numeric_limits::max(), '\n');


	int _k;
	cin >> _k;
	cin.ignore(std::numeric_limits::max(), '\n');



	res = ballAllocate(_m, _n, _k);
	cout << res << endl;

	return 0;

}

你可能感兴趣的:(算法编程)