把M个同样的宝石放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(其中1,5,1和5,1,1代表同一种方法),用C++实现
注意最后是输出n行字符,不是算出来有几种分法。假设输入为7, 3
示例:
如输入7,3,输出为8行:
7,0,0
6,1,0
5,2,0
4,3,0
5,1,1
4,2,1
3,3,1
3,2,2
思路:
本题可以使用递归法,也是深度优先搜索,遍历到所有满足条件的分法,存入vector中,将其从大到小排序后,放到set里,利用set元素不重复的特点进行去重。有2点需要说明:
①之所以使用set而不是unordered_set,是因为set的元素是按照字典序排序的,方便输出结果时控制顺序。而unordered_set是按照哈希表的顺序排序的,肉眼看起来跟乱序一样。
②set的元素类型可以用vector,也可以将vector的元素都转化成string存,因为他们都有严格顺序。
③如果是用vector直接存入set中,那输出时可以选择逆序输出,这样就和例子给的“从大到小”的顺序一致了。
代码如下:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <string>
using namespace std;
void generatePartitions(int gems, int plates, vector<int>& partitions, set<vector<int>>& uniquePartitions)
{
if (plates == 1)
{//分到最后一个盘子了
partitions.push_back(gems);
vector<int> tempPartitions = partitions;
sort(tempPartitions.begin(), tempPartitions.end(), [](int a, int b){return a > b;});
uniquePartitions.insert(tempPartitions);//将排序后的结果存入uniquePartitions中
partitions.pop_back();//回溯
return;
}
for (int i = 0; i <= gems; ++i) {
partitions.push_back(i);
generatePartitions(gems - i, plates - 1, partitions, uniquePartitions);//递归
partitions.pop_back();//回溯
}
}
int main() {
int gems = 7; // 宝石的数量
int plates = 3; // 盘子的数量
vector<int> partitions;//每轮分宝石的方案
set<vector<int>> uniquePartitions;//存所有可行方案的set
generatePartitions(gems, plates, partitions, uniquePartitions);
// 逆向打印set中的所有方案
for (auto iter = uniquePartitions.rbegin(); iter != uniquePartitions.rend(); ++iter)
{
cout << (*iter)[0] << "," << (*iter)[1] << "," << (*iter)[2] << endl;
}
return 0;
}
输出是:
7,0,0
6,1,0
5,2,0
5,1,1
4,3,0
4,2,1
3,3,1
3,2,2