题目90:整数划分

70 篇文章 1 订阅

题目链接:

http://acm.nyist.net/JudgeOnline/problem.php?pid=90

描述

将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,
其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不
同划分个数。
例如正整数6有如下11种不同的划分:
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。

输入

第一行是测试数据的数目M(1<=M<=10)。以下每行均包含一个整数n(1<=n<=10)。

输出

输出每组测试数据有多少种分法。

样例输入

1
6

样例输出

11

算法思想:

生成函数解法(组合数学中应用的母函数)。
先简单说明一下生成函数吧,下面是一个生成函数,
这里写图片描述
xk的系数ak代表了可获得数字k的组合数。
那么回到我们的问题中,我们怎么用生成函数去解决呢?

类似的,我们可以计算生成函数:
这里写图片描述
我们来做一个说明,第i个括号(1+xi+x2i+x3i · · ·)选择的元素代表了数字i在我们最终的划分中出现的次数,具体而言,如果我们在第i个括号中选择了元素 xc(i) * i 则表示数字 i 在我们最终的划分中出现了c(i)次。如果我们把最终从每个括号里面选择出来的元素相乘 x1 * c(1) · x2 * c(2) · x3 * c(3) · · · = x c(1) + 2 * c(2) + 3 * c(3)···.那么,xn的系数就是我们可以获得多少种不同的方式使得c(1) + 2 * c(2) + 3 * c(3)··· = n,也就是n的划分数(其中,c(i)代表了在一次划分中数字 i 的出现次数)。比如说25=6+4+4+3+2+2+2+1+1,用上式表示就是25=1(2)+2(3)+3(1)+4(2)+5(0)+6(1),也就是在划分中有两个1,三个2,一个3,两个4,0个5以及一个6。
假设x<1,那么我们可以将上面的生成函数表示为:
这里写图片描述
考虑到我们能选择的最大的数 i 是 m ,所以真正计算的时候我们需要对上面的生成函数式子做一下修改:

G(x) = (1+x+x^2+x^3+…+x^n) (1+x^2+x^4+…) (1+x^3+x^6+…) … (1+x^m)
= g(x,1) g(x,2) g(x,3) … g(x, n)

源代码

/*
Author:杨林峰
Date:2017.11.28
NYOJ(90):整数划分
*/

#include <iostream> 
#include <cstring>
using namespace std;
#define N 11
 unsigned long a[N];/*多项式a的系数数组*/
 unsigned long b[N];/*多项式b的系数数组*/
 unsigned long c[N];/*存储多项式a*b的结果*/

  /*两个多项式进行乘法,系数分别在a和b中,结果保存到c ,项最大次数到N */
  /*注意这里我们只需要计算到前N项就够了。*/
 void Poly()
 {
    int i, j;
    memset(c, 0, sizeof(c));
    for (i = 0; i<N; i++)
    for (j = 0; j<N - i; j++) /*y<N-i: 确保i+j不会越界*/
    c[i + j] += a[i] * b[j];
 }

  /*计算出前N项系数!即g(x,1) g(x,2)... g(x,n)的展开结果*/
 void Init()
 {
      int i, k;
      memset(a, 0, sizeof(a));
      memset(c, 0, sizeof(c));
     for (i = 0; i<N; i++) a[i] = 1; /*第一个多项式:g(x, 1) = x^0 + x^1 + x^2 + x^3 +  */
     for (k = 2; k<N; k++)
     {
        memset(b, 0, sizeof(b));
        for (i = 0; i<N; i += k) b[i] = 1;/*第k个多项式:g(x, k) = x^0 + x^(k) + x^(2k) + x^(3k) +  */
        Poly(); /* 多项式乘法:c= a*b */
        memcpy(a, c, sizeof(c)); /*把相乘的结果从c复制到a中:c=a; */
    }
  }

 int main()
 {
     int M, n;
     cin >> M;
     while (M--)
     {
         cin >> n;
         Init();
         cout << c[n] << endl;
     }
     return 0;
 }        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值