法1:dfs暴搜
数据量很小,都没必要剪枝。
法2:线性dp
1.定义状态:$dp(i, j)表示i个能量分给j$个分身的集合,属性是个数。
2.状态转移:$dp(i, j) = dp(i,j-1) + dp(i - j,j)(i >= j)(属性是个数的话,一般都是集合相加)$
集合分析法,最后一步不同的是能量如何分配(这里比较奇怪):
(1)一部分由至少有一个分身分配到的能量为0的集合,那么至少有一个没分配到:$dp(i,j-1)$(全部都没分到的情况不用单独列,因为至少有一个没分到的集合已经包含了);
(2)另一部分是全部分身都分配到了能量的集合,至少每个都分配到了一点能量:$dp(i-j,j)$(有分身分的多的情况也不用另外讨论,因为至少每个都分到1点能量也包含了)。
3.初始化:$dp(0,0) = 0(0能量分给0分身只有一种情况)$
4.递推顺序:$i要从0开始递推,因为能量可以为0。$
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<stack>
#include<climits>
#define x first
#define y second
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int t, ans, m, n;
const int N = 10 + 5;
int dp[N][N];
//f(i,j) 能量为i,分给j个人的组合数
//f(i,j) = f(i-1, j) + f(i - j, j)
//f(0, 0) = 1
int main(void){
cin >> t;
dp[0][0] = 1;
for (int i = 0; i <= 10; ++i){
for (int j = 1; j <= 10; ++j){
dp[i][j] = dp[i][j - 1] + (i - j >= 0 ? dp[i - j][j] : 0);
}
}
while (t--){
cin >> m >> n;
ans = 0;
cout << dp[m][n] << endl;
}
return 0;
}