• nyoj_90_整数划分_201403161553


     

    整数划分

    时间限制:3000 ms  |  内存限制:65535 KB
    难度:3
     
    描述
    将正整数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
    来源
    [苗栋栋]原创
    上传者
    苗栋栋
     

    此题可以用递归和动态规划两种方法来解决,首先介绍动态规划的,数组dp[N][M]表示N为被划分数,M为划分数的最大值,此题M==N,故即求dp[N][N];

    1>状态转移方程:

    dp[N][M]=dp[N][M-1]+dp[N-M][M];

    该怎样理解呢?这里分两步:

    Step 1:所划分的最大数不包括M,即每个划分数都是小于M的,此时总数为dp[N][M-1].

    Step 2:所划分的最大数包括M,那么这一步被划分数就应该减去一个M,此时总数为dp[N-M][M].

    到这里就是完整的思路了,应该注意的是上面的划分,划分数里有重复的数,那么如果要求划分数没有重复的呢,该怎样求呢?

    这里的状态转移方程和上面就有点细微区别了.先来看看方程:

    2>dp[N][M]=dp[N][M-1]+dp[N-M][M-1];

    其实联系1中的步骤就不难理解了,同样分为两步:

    Step 1:所划分的最大数不包括M,即每个划分数都是小于M的,此时总数也是dp[N][M-1].

    Step 2:所划分的最大数包括M,那么划分就的相应的减去M,注意到不能重复,即M划分数出现的次数只能为1.所以M就得换成M-1了,即dp[N-M][M-1].

    3>在拓展一下,要是划分的个数为确定的数呢?即dp[N][K].表示N被划分成K个数.

    这时状态转移方程就为

    dp[N][K]=dp[N-K][K]+dp[N-1][K-1].

    应该这样理解:

    Step 1:被划分的K个数中不包括1,那么就应该先自动的为其分配1,K个数共N-K,剩下的数自由分配,总能保证其值大于2,即dp[N-K][K].

    Step 2:存在一个数为1的情况,此时剩下的N-1分给K-1个数,即dp[N-1][K-1].

    代码如下:

     1 #include <stdio.h>
     2 #include <string.h>
     3 int dp[12][12];
     4 int main()
     5 {
     6     int T;
     7     scanf("%d",&T);
     8     while(T--)
     9     {
    10         int i,j,n;
    11         memset(dp,0,sizeof(dp));
    12         scanf("%d",&n);
    13         for(i=1;i<=n;i++)
    14         {
    15             for(j=1;j<=n;j++)
    16             {
    17                 if(i>j)
    18                 dp[i][j]=dp[i-j][j]+dp[i][j-1];
    19                 else if(i==j)
    20                 dp[i][j]=dp[i][j-1]+1;
    21                 else if(i<j)
    22                 dp[i][j]=dp[i][i];
    23             }
    24         }
    25         printf("%d
    ",dp[n][n]);
    26     }
    27     return 0;
    28 }

     

    下面来介绍递归的方法:

    整数划分问题是算法中的一个经典命题之一,有关这个问题的讲述在讲解到递归时基本都将涉及。所谓整数划分,是指把一个正整数n写成如下形式:

           n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,...,mi}为n的一个划分。

           如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);

           例如当n=4时,他有5个划分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};

           注意4=1+3 和 4=3+1被认为是同一个划分。

           该问题是求出n的所有划分个数,即f(n, n)。下面我们考虑求f(n,m)的方法;

    1.递归法:

           根据n和m的关系,考虑以下几种情况:

            (1)当n=1时,不论m的值为多少(m>0),只有一种划分即{1};

            (2) 当m=1时,不论n的值为多少,只有一种划分即n个1,{1,1,1,...,1};

            (3) 当n=m时,根据划分中是否包含n,可以分为两种情况:

                  (a). 划分中包含n的情况,只有一个即{n};

                  (b). 划分中不包含n的情况,这时划分中最大的数字也一定比n小,即n的所有(n-1)划分。

                  因此 f(n,n) =1 + f(n,n-1);

            (4) 当n<m时,由于划分中不可能出现负数,因此就相当于f(n,n);

            (5) 但n>m时,根据划分中是否包含最大值m,可以分为两种情况:

                   (a). 划分中包含m的情况,即{m, {x1,x2,...xi}}, 其中{x1,x2,... xi} 的和为n-m,因此这种情况下

                     为f(n-m,m)

                   (b). 划分中不包含m的情况,则划分中所有值都比m小,即n的(m-1)划分,个数为f(n,m-1);

                  因此 f(n, m) = f(n-m, m)+f(n,m-1);

          综上所述:

                                 f(n, m)=   1;                (n=1 or m=1)

                                 f(n, n);                        (n<m)

                                 1+ f(n, m-1);                (n=m)

                                 f(n-m,m)+f(n,m-1);      (n>m)

    代码如下:

     1 #include <stdio.h>
     2 int f(int n,int m)  // fun(n, m)表示将整数 n 划分为最大数不超过 m 的划分
     3 {
     4     if(n==1||m==1)
     5     return 1;
     6     else if(m>n)
     7     return f(n,n);
     8     else if(m==n)// 此时也是两部分,如果含有 m 则只有一种只含有 m 的划分,如果不含有 m 则转化为最大数不超过 m-1 的划分
     9     return f(n,m-1)+1;
    10     else if(m<n)    // 此时将问题转化为两部分  1.划分中含有 m;   2.划分中不含 m
    11     return f(n,m-1)+f(n-m,m);
    12 }
    13 int main()
    14 {
    15     int T;
    16     scanf("%d",&T);
    17     while(T--)
    18     {
    19         int n;
    20         scanf("%d",&n);
    21         printf("%d
    ",f(n,n));
    22     }
    23     return 0;
    24 }
  • 相关阅读:
    BlockingQueue(阻塞队列)详解
    支付宝系统架构(内部架构图)
    微博的消息队列
    JVM源码分析之堆外内存完全解读
    滑动冲突的补充——Event的流程走向
    BaseFragment的定义—所有Fragment的父类
    BaseActivity的定义——作为所有Activity类的父类
    BGARefreshLayout-Android-master的简单使用
    分析BGARefreshLayout-master
    简便数据库——ORMLite框架
  • 原文地址:https://www.cnblogs.com/xl1027515989/p/3603533.html
Copyright © 2020-2023  润新知