【做题笔记】P1025数的划分

可以说是这个题的一个变种,然而直接成了黄题是什么鬼(((

注:上面那篇blog里对于 dfs 的讲解不是很到位,所以这次就当是修一下锅www/wq


首先,我们发现这个玩意和 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;
}

猜你喜欢

转载自www.cnblogs.com/BlueInRed/p/12617587.html
今日推荐