/*
* 题目描述:
* dota游戏里面,召唤师可以控制冰雷火三种元素,并通过元素组合
* 产生新的技能。现在我们修改了一张新的地图,地图中他能够控制n种元素,
* 并且将m个元素围成一个圈组成一个新技能(这m个元素通过旋转或翻转,算
作重复,如123、231、312、321、213、132都算重复),那么召唤师能组合
多少级能(20000>=n>=1, 1<=m<=10000),由于结果可能很大,请将结果对
1000000007取余
输入描述:
n和m
输出描述:
组合的技能数
示例1:
输入:
3 3
输出:
10
说明:
111、112、113、122、123、133、222、223、233、333一共10种
*/
说明:这道题有多种解法,一开始我是按照全排列的回溯方法来做,超时,后来我又试了结合数学计算的递归方法,最后将递归变为从底至上的方法可以解决超时问题
方法1:全排列的回溯方法
该方法适合返回组合的数组,但是对于这种返回个数的题,时间复杂度太高
代码如下:
#include<iostream>
#include<vector>
using namespace std;
int res = 0;
void back_tracking(int n, int m, int level, int start, vector<int>& tmp) {
//cout << start << " ";
if (level == m) {
res++;
/*
for (int i = 0;i < tmp.size(); ++i) {
if (i == 0) cout << tmp[0];
else cout << " " << tmp[i];
}
cout << endl;
*/
return;
}
for (int i = start; i <= n; ++i) {
tmp.push_back(i);
back_tracking(n, m, level + 1, i, tmp);
tmp.pop_back();
}
}
int solution(int n, int m) {
vector<int> tmp = {};
back_tracking(n, m, 0, 1, tmp);
return res;
}
int main() {
// 共有n个元素, m个元素组成新技能
int n, m;
cin >> n >> m;
cout << solution(n, m) << endl;
}
方法2:数学计算的DP方法
参考其他博主的链接:有n个数,从中取m个数,可以重复取,有多少种组合
- 数学方法1:分类讨论:
- 数学方法2:
一共有n个数,假设第i个数出现了Xi次,也就是X1+X2+...+Xi+...+Xn=m。所有元素的个数和为m。该问题转化为求有多少个不同的个数和为m的非负整数解,转化成了将n个小球放进m个箱子里的问题()C(m+n-1, m)
参考链接:可重复选元素的组合问题(21)
动态规划具体思路:
1、初始化:定义dp[n+1][m+1]大小的数组,dp[i][j]代表了从i个元素中选取j个元素的组合数;
当i=0,j=0时,dp[0][0] =1;
当i=0, j不等于0时,dp[0][j] = 0,代表从0个元素中选取非0个元素的组合数为0
当i不等于0,j=0时,dp[i][0] = 1,代表从非0个元素中选取0个元素的组合数为1
2、状态转移方程:
dp[1][1] = 1, 有组合1
dp[1][2] = 1,有组合11
dp[2][1] = 2, 有组合1, 2
dp[2][2] = 3,有组合11,22,12
...
可发现规律:
(我目前无法从理解的角度给出解释)
代码如下:
#include<iostream>
#include<vector>
using namespace std;
int solution(int n, int m) {
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 0; i <= n; ++i) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[n][m];
}
int main() {
int n, m;
cin >> n >> m;
cout << solution(n, m) << endl;
}
可以进行空间压缩:n*m->m
代码如下:
int solution_1(int n, int m) {
vector<int> dp(m + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
dp[j] = dp[j] + dp[j - 1];
}
}
return dp[m];
}
int main() {
int n, m;
cin >> n >> m;
//cout << solution(n, m) << endl;
cout << solution_1(n, m) << endl;
}