整数拆分

整数拆分

今天学习了一个叫做整数拆分的算法

问题描述
给定一个整数n,输出这个整数拆分的可能总数


递归写法

先上完整代码:

/**
 * 整数n 的拆分次数
 * @param  n 被拆分的次数
 * @param  m 每次拆分的最大数值
 * @return   拆分次数
 */
int rec(int n, int m) {
    if (n==1 || m==1)
        return 1;
    if (n < m) return rec(n, n);
    if (n == m) return rec(n, m-1) + 1;
    if (n > m) {
        return rec(n, m-1) + rec(n - m, m);
    } 
}

变量解释:
n 『自然是我们要分解的数字』
m 举个例子来说明:

6 = 5 + 1 = 4 + 2 = 4 + 1 + 1 = ....
m 是 每次被拆分的 最大的数值,上面的例子m 就会等于 5, 4等。

尝试解说:

  • 当一个被拆分数n等于1时,自然只有一种分解方法,就是『1』,m等于1时,最大拆分数m=1,自然只有一种分解方法『1,1,1,…』
  • 当n < m 时, 最大拆分数>被拆分数,不用说 这种情况是无意义的,自然只是返回 rec(n, n)
  • 当n == m 时, 既是rec(n, n) 拆分成一个大情况rec(n, n-1)和一个单个例子『n』
  • 当 n > m 时,也是有两种情况,要么用m进行拆分,要么用比m小的数进行拆分

母函数解法

先把问题理清楚:

我们现在是要将可以组成n的所有情况找出来,那么需要的是枚举出所有应该出现的组合,然后统计总数。

该算法的基本思路

6 的所有组合
『5,1』
『4,2 』,『4,1,1』
『3,3 』, 『3,2,1』, 『 3,1,1,1』
『2,2,2』, 『 2,2,1,1』,『 2,1,1,1,1』
『1,1,1,1,1,1』

我们现采用的是这样一种枚举方法:
1.现在把所有可能出现在组合中的数字的先列出来『1,2,3,4,5,…,n』,这些数字都有可能出现在组合中是不是?

2.然后,我先来假定一个表达式 x i * k ,x是什么不用管,i 表示为当前可组成n的数字,k表示数字i的重复次数,自然 k < n / i

3.最后就是为什么要写成 x i* k的这么难看的表达式的原因了,直接上例子

Example:
照样用6来做例子:
1,2,3,4,5,6 可能出现的数字

这里是数字1的所有可能重复的情况:
x 1 * 0, x 1 * 1,x 1 * 2,x 1 * 3,x 1 * 4,x 1 * 5,x 1 * 6

这里是数字2的所有可能重复的情况:
x 2 * 0, x 2 * 1,x 2 * 2,x 2 * 3,

这里是数字3的所有可能重复的情况:
x 3 * 0, x 3 * 1,x 3 * 2

这里是数字4的所有可能重复的情况:
x 4 * 0, x 4 * 1

这里是数字5的所有可能重复的情况:
x 5 * 0, x 5 * 1

这里是数字6的所有可能重复的情况:
x 6 * 0, x 6 * 1

(1+ x 1+x 2+x 3+x 4+x 5+x 6)* (1+ x 2 +x 2 * 2+x 2 * 3) * ( 1+x 3 +x 3 * 2) * (1+ x 4) * ( 1+x 5) * ( 1+x 6

然后进行因式分解,你会发现一个很神奇的东西:
取每个括号中的其中一个来看看

(x 2) * ( 1) * ( 1) * (x 4) * (1) * (1) = x n
这就是6的其中一个组合了

1重复了两次,2重复了0次,3重复了0次,4重复了1次,5重复了0次,6重复了0次

我们只要把分解因式后的 x n的系数找到即可

然后你就可以理解这个公式了:

G(x)=(1+x^1+x^2+…+x^n)*(1+x^2+x^4+…+x^((n/2)*2))*…*(1+x^m+x^(2*m)+x^(3*m)+…+x^((n/m)*m))*…*(1+x^n)

其他不多说上代码

// 多项式乘法,只计算系数
void ploy() {
    memset(c, 0, sizeof(c));
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            c[i+j] += a[i] * b[j];
}

void init() {
    memset(c, 0, sizeof(c));
    memset(a, 0, sizeof(a));
    for (int i=0; i<N; i++) a[i] = 1;

    for(int k=2; k<N; k++) {
        memset(b, 0, sizeof(b));
        for(int i=0; i<N; i+=k) b[i] = 1;
        ploy();
        memcpy(a, c, sizeof(c));
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值