蓝桥杯-数的划分(DFS)
0.题目
问题描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
输入格式
n,k
输出格式
一个整数,即不同的分法
样例输入
7 3
样例输出
4
数据规模和约定
6<n<=200,2<=k<=6
1.题解
1.1 DFS暴力搜索+剪枝
思路
这里自上而下,一个数一个数的确定,每个数大于他前一个数。
记录位数,和,前置值
1.位数用于判断最终选择到了k个数
2.和用于判断结果是否满足n
3.前置值用于剪枝,
代码
#include<bits/stdc++.h>
using namespace std;
int n, k;
int ans = 0;
void dfs(int x, int sum, int pre) {
if(x == k) {
if(sum == n)
ans++;
return;
}
// 这里除去之前sum中的x个,自己的一个。剩余k - x - 1个,我们可以计算出当前值最大可能值(每个均不为空)
// 由于禁止重复,我们按从小到大的方向前行,后面的必须大于等于前面的就不会重复了
for(int i = pre; i <= n - sum - (k - x - 1) * pre; i++) {
dfs(x+1, sum+i, i);
}
}
int main() {
cin >> n >> k;
dfs(0, 0, 1);
cout << ans;
return 0;
}
1.2 划分+递归
思路
明确dfs函数的定义!!!即将n个数划分给m个数
所以截止条件就比较明确了,这里又分为结束的合理情况和不合理情况
1.先判断不合理情况,给下面的合理情况做一波排除: 1.1 n == 0(不可能将0个数分配给其他任意个数的人) 1.2 m0(至少要分配给一个人) 1.3 n<m(不够分配了,一次分配至少要m个) 其实1.1和1.2均可以通过2.中的合理条件排除,这里为了方便理解,也就写了出来(可以省略写前两个条件)
2.合理情况:m1(剩下的全部分配给该人); n==m(由于一次分配至少要分配m个,刚好全部分配)
主要划分为两种情况,均针对当前还在变化的首位数:
1.如果继续增加(不选择1),那就是给所有人均分配一个,然后继续给m个人分配 dfs(n-m,m)
2.如果不继续增加(选择1), 那就是给他分配一个,之后的n-1个留给m-1个人继续分配 dfs(n-1,m-1)
代码
#include<bits/stdc++.h>
using namespace std;
// 将n划分给m个数(每个至少为1,因为后面的总要大于前面的,前面第一个分了,后面就都至少要分1个),一定要先明确递归函数的定义!!!
int dfs(int n, int m) {
// 不合理条件: 1. n<m(不够分); 2.m==0,没有对象给予了 3. n==0没有数分
if (n < m || m == 0 || n == 0) return 0;
// 合理结束条件: 1.n == m(刚好够一次分的) 2.m==1, 全分给剩下的这个
if (n == m || m == 1) return 1;
// 如果不选择 1, 就每个人都分一个1, 然后将剩下的 n-m个再分配给 m个人
// 如果选择某个层级首个位置固定为1(之后不再选择), 那么就将剩下的 n-1个再分配给剩下的 m-1个人
return dfs(n-m,m) + dfs(n-1,m-1);
}
int main() {
int n, k;
cin >> n >> k;
cout << dfs(n, k);
return 0;
}