数的划分
Description
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
Input
有多则测试数据。
对于每组测试数据,仅有一行,包括两个整数n,k (6<n<=200,2<=k<=6)。
Output
对于每组测试数据,输出一个整数,即不同的分法。
Sample Input
7 3
Sample Output
4
Hint
输入: 7 3
输出:4 {四种分法为:1,1,5; 1,2,4; 1,3,3; 2,2,3;}
思路(dp)
它的划分时候子划分而来的,比如说7,4他是由6,4划分而来的。
搜索状态转移,考虑两种情况,
1.划分包含1的情况dp(n-1, k - 1); 就相当于把1单独拿出来
2.划分不包含1的情况dp(n-k, k); 相当于将n-m分成m个数,每个数至少大于1.
我们知道dp(i, 1) 和dp(i,i)是肯定为1的,因为任何一个数的1个划分是它本身,k个数的k个划分也只有一种情况
AC代码
/**
dp O(n * m)
**/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 205;
int dp[maxn][maxn];
void solve(){
int n, k;
scanf("%d%d", &n, &k);
memset(dp, 0, sizeof(dp));
//每个数化分成1个只有一种
for (int i = 1; i <= n;++i) dp[i][1] = 1;
for (int i = 2; i <= n; ++i){
for (int j = 2; j <= k && j <= i; ++j){
dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];
}
}
printf("%d\n", dp[n][k]);
}
int main(){
solve();
return 0;
}
思路(搜索)
和dp是一个道理,但是是递归实现的,重复算了很多数据
AC代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int dfs(int n, int k){
if (k == 1 || n == k) return 1;
if (k <= 0 || n <= 0 || n < k) return 0;
return dfs(n - 1, k - 1) + dfs(n - k, k);
}
void solve(){
int n, k;
scanf("%d%d", &n, &k);
int ans = dfs(n, k);
printf("%d\n", ans);
}
int main(){
solve();
return 0;
}
思路(记忆化搜索)
搜索过的状态记录下来,下一次搜索到了就直接加上即可。
AC代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 205;
int dp[maxn][maxn];
int dfs(int n, int k){
if (dp[n][k] >= 0) return dp[n][k];
dp[n][k] = 0;
if (n - 1 > 0 && k - 1 > 0){
dp[n][k] += dfs(n -1, k - 1);
}
if (n - k > 0 && k > 0){
dp[n][k] += dfs(n - k, k);
}
return dp[n][k];
}
void solve(){
int n, k;
scanf("%d%d", &n, &k);
memset(dp, -1, sizeof(dp));
//每个数化分成1个只有一种
for (int i = 1; i <= n;++i) dp[i][1] = 1;
int ans = dfs(n, k);
printf("%d\n", ans);
}
int main(){
solve();
return 0;
}