可以说是这个题的一个变种,然而直接成了黄题是什么鬼(((
注:上面那篇blog里对于 dfs 的讲解不是很到位,所以这次就当是修一下锅www
首先,我们发现这个玩意和 dfs 的搞法一毛一样,所以果断考虑 dfs !
如果直接去遍历所有的状态,不仅耗时,而且难以辨别重复。
那么我们发现,按照题目的要求,也就是让我们找到有多少个本质不同的组合(即构成组合的数字不同)。
那么我们发现:在一种本质相同的组合中,一定有一种组合保证升序排列。
例如:我们现在要把 10 分成 5 份,那么对于一种本质相同的排列:
2 1 7
2 7 1
7 2 1
7 1 2
1 2 7 //升序
......
那么这个本质相同的组合中升序排列就是1 2 7
这一种。
由于我们只需要知道有所少个本质不同的组合,所以我们只要找到一个本质相同的组合中升序排列的组合即可!
如何构造一组升序排列?
如果我们在递归的过程中记录一个 start
参数表示我们在上一层递归中选的数,那么只要任选一个 \(i\) ,使得 \(start\leq i\) 即可(注意到可以不是单调递增的,i.e. 允许构造出的序列存在重复值)!
那么只要遍历所有的 \(i(start\leq i \leq n)\) ,然后递归即可。
参考代码(60分):
#include <cstdio>
#include <algorithm>
int n, k, ans;
void dfs(int start, int num, int x) {
//num表示当前的序列之和,x表示当前有x个数在序列里
if(x==k) {
if(num==n)
++ans;
return ;
}
for(int i=start;i<=n;i++)
dfs(i, num+i, x+1);
}
int main(void) {
scanf("%d%d", &n, &k);
dfs(1, 0, 0);
printf("%d\n", ans);
return 0;
}
考虑如何通过剪枝优化。
剪枝的核心思想是排除当前和当前之后根本没有可能满足条件的解。那么我们发现,若当前序列中的元素总和 \(x\) 加上现在尝试的 \(i\) 已经大于我们要拆分的 \(n\) 了,那么就可以果断退出(因为后面的 \(i\) 是越来越大的)!
于是有AC代码:
#include <cstdio>
#include <algorithm>
int n, k, ans;
void dfs(int start, int num, int x) {
if(x==k) {
if(num==n)
++ans;
return ;
}
for(int i=start;num+i<=n;i++)
dfs(i, num+i, x+1);
}
int main(void) {
scanf("%d%d", &n, &k);
dfs(1, 0, 0);
printf("%d\n", ans);
return 0;
}
原文:https://www.cnblogs.com/BlueInRed/p/12617587.html