• 整数n划分成k个不为0的部分


    题目:洛谷P1025

    解法:

    看到这个题目,我第一反应是组合数学的知识,题目等价于将n个相同的物品(数字1)分成k堆,求方案数。

    这道题的解法在高中的排列组合肯定用到过,但是想不起来了。

    方法如下:

    首先,要保证计算过程中相同的方案(每堆的数量相同,但顺序不同)只能计数1次,我采用的是在前一堆的基础上增加后一堆的数量,保证后一堆的数量不小于前一堆,这样每堆的数量是递增的,也就不会担心相同数量的堆顺序不同而重复计数了。

    那么,怎样保证后一堆的数量不小于前一堆呢?可以直接考虑后一堆的数量=前一堆的数量+i(i>=0),这样迭代的话需要k-1个循环,感觉不太好做,放弃了。

    之后,我采用了少量多次,每次分配的堆数不超过上一次的办法确保后一堆数量不小于前一堆。大体思路是,先分配给k个堆相同的数量i,再分配给h个堆(h<k)相同的数量j(j>=0),令k=h,h更新,继续进行。一直进行下去,直到可分配的数量变为0或者只剩1堆可以分配,那么一种分配方案就产生了。

    可以看出,这是一种递归的算法。

    每一次操作的h、j不同,产生的解决方案也就不同。

    而可以发现,当j>1时,分给h个堆的相同数量j可以分成分给h个堆的相同数量1,分j次。取j>1会使我们漏掉一些分配方案。而取j=0相当于什么都没干。

    所以我们将,下次分配的数量定为1,当下次分配的数量j确定了,变量也就剩下次分配的堆数h。

    分配的堆数h满足1<=h<=k,要保证下次分配的堆的数量小于当前堆的数量,那么我们令h=k-下次不打算分配的堆数i(0<=i<k)

    大体上的思路搞定了,但是题目要求每堆不为空,而如果第一次分配的数目就小于k,那么是不符合题意的。所以,现将k堆分配数量1,可分配的数量变成n=n-k,堆数k=k,之后再递归地进行上述的过程。

    代码如下:

    #include <stdio.h>
    
    long long int sum = 0;//方案数
    
    void div(int n, int k)//n,k代表可分配的数量和分配的堆数
    {
        if (k < 1 || n < 0)//计算过程中,可能存在这种情况,这样的分配方案是不可行的
            return;
        if (k == 1 || n == 0)//当只剩下一个可以分配的堆或者可分配数量都已经分配完了,代表一种方案产生
    
        {
            sum++;
            return;
        }
        for (int i = 0; i < k; i++)//i代表下次不分配的堆个数,对不同的i值进行递归
            div(n - (k-i), k - i);
    }
    
    int main()
    {
        int n = 0, k = 0;
        scanf("%d", &n);
        scanf("%d", &k);
        div(n - k, k);//k堆都分数量1,确保k堆都不为0
        printf("%d", sum);
        return 0;
    }
  • 相关阅读:
    ff与ie 的关于js兼容性
    CSS清除浮动的方法
    java8 LocalDateTime
    BigDecimal
    JAVA将 Word 文档转换为 PDF
    Ionic4
    SpringBoot后端统一格式返回
    SpringBoot集成JWT
    Java Lombok
    SpringBoot 中通过 CORS 解决跨域问题
  • 原文地址:https://www.cnblogs.com/lylhome/p/13254991.html
Copyright © 2020-2023  润新知