zoukankan      html  css  js  c++  java
  • 整数划分类型题目

    整数划分 --- 一个老生长谈的问题:
      1) 练练组合数学能力.
      2) 练练递归思想
      3) 练练DP
      总之是一道经典的不能再经典的题目:
      这道好题求:
      1. 将n划分成若干正整数之和的划分数。
      2. 将n划分成k个正整数之和的划分数。
      3. 将n划分成最大数不超过k的划分数。
      4. 将n划分成若干奇正整数之和的划分数。
      5. 将n划分成若干不同整数之和的划分数。

     

    1.将n划分成不大于m的划分法: 

       1).若是划分多个整数可以存在相同的:

        dp[n][m]= dp[n][m-1]+ dp[n-m][m]  dp[n][m]表示整数 n 的划分中,每个数不大于 m 的划分数。
           则划分数可以分为两种情况:
           a.划分中每个数都小于 m,相当于每个数不大于 m- 1, 故划分数为 dp[n][m-1].
           b.划分中有一个数为 m. 那就在 n中减去 m ,剩下的就相当于把 n-m 进行划分, 故划分数为 dp[n-m][m];

      2).若是划分多个不同的整数:

      dp[n][m]= dp[n][m-1]+ dp[n-m][m-1]   dp[n][m]表示整数 n 的划分中,每个数不大于 m 的划分数。
          同样划分情况分为两种情况:
          a.划分中每个数都小于m,相当于每个数不大于 m-1,划分数为 dp[n][m-1].
          b.划分中有一个数为 m.在n中减去m,剩下相当对n-m进行划分,

       并且每一个数不大于m-1,故划分数为 dp[n-m][m-1]

      2.将n划分成k个数的划分法:

        dp[n][k]= dp[n-k][k]+ dp[n-1][k-1];

         方法可以分为两类:
           第一类: n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 k 个 1 分
         到每一份,然后再把剩下的 n- k 分成 k 份即可,分法有: dp[n-k][k]
           第二类: n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩
         下的 n- 1 再分成 k- 1 份即可,分法有:dp[n-1][k-1]

      

      3.将n划分成若干奇数的划分法:(不懂)

        g[i][j]:将i划分为j个偶数

        f[i][j]:将i划分为j个奇数
         g[i][j] = f[i - j][j];
         f[i][j] = f[i - 1][j - 1] + g[i - j][j];

             将n划分成若干奇数的划分法:

          f[i][j]:将i划分为j个偶数

          g[i][j]:将i划分为j个奇数
         f[i][j] = g[i - j][j];
         g[i][j] = g[i - 1][j - 1] + f[i - j][j];

        方法可以分为两类:

          对于偶数f[i][j]: i 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 j 个 1 分

        到每一份,然后再把剩下的 i - j 分成 j 个奇数即可,分法有: g[i-j][j]

           对于奇数g[i][j]: i 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩

        下的 n-1 再分成 j- 1 份即g[i - 1][j - 1],还有就是没有一份为 1 的分法可以先拿出 j 个 1 分

        到每一份,然后再把剩下的i - j 分成 j 个偶数即f[i-j][j],分法有:g[i-1][j-1] + f[i-j][j]

    /*
     * hit1402.c
     *
     *  Created on: 2011-10-11
     *      Author: bjfuwangzhu
     */
    
    #include<stdio.h>
    #include<string.h>
    #define nmax 51
    int num[nmax][nmax]; //将i划分为不大于j的个数
    int num1[nmax][nmax]; //将i划分为不大于j的不同的数
    int num2[nmax][nmax]; //将i划分为j个数
    int f[nmax][nmax]; //将i划分为j个奇数
    int g[nmax][nmax]; //将i划分为j个偶数
    void init() {
        int i, j;
        for (i = 0; i < nmax; i++) {
            num[i][0] = 0, num[0][i] = 0, num1[i][0] = 0, num1[0][i] = 0, num2[i][0] =
                    0, num2[0][i] = 0;
        }
        for (i = 1; i < nmax; i++) {
            for (j = 1; j < nmax; j++) {
                if (i < j) {
                    num[i][j] = num[i][i];
                    num1[i][j] = num1[i][i];
                    num2[i][j] = 0;
                } else if (i == j) {
                    num[i][j] = num[i][j - 1] + 1;
                    num1[i][j] = num1[i][j - 1] + 1;
                    num2[i][j] = 1;
    
                } else {
                    num[i][j] = num[i][j - 1] + num[i - j][j];
                    num1[i][j] = num1[i][j - 1] + num1[i - j][j - 1];
                    num2[i][j] = num2[i - 1][j - 1] + num2[i - j][j];
                }
            }
        }
        f[0][0] = 1, g[0][0] = 1;
        for (i = 1; i < nmax; i++) {
            for (j = 1; j <= i; j++) {
                g[i][j] = f[i - j][j];
                f[i][j] = f[i - 1][j - 1] + g[i - j][j];
            }
        }
    }
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.in", "r", stdin);
    #endif
        int n, k, i, res0, res1, res2, res3, res4;
        init();
        while (~scanf("%d %d", &n, &k)) {
            res0 = num[n][n];
            res1 = num2[n][k];
            res2 = num[n][k];
            for (i = 0, res3 = 0; i <= n; i++) {
                res3 += f[n][i];
            }
            res4 = num1[n][n];
            printf("%d
    %d
    %d
    %d
    %d
    
    ", res0, res1, res2, res3, res4);
        }
        return 0;
    }
    View Code

    将正整数划分成连续的正整数之和
    如15可以划分成4种连续整数相加的形式:
    15
    7 8
    4 5 6
    1 2 3 4 5

        首先考虑一般的形式,设n为被划分的正整数,x为划分后最小的整数,如果n有一种划分,那么
    结果就是x,如果有两种划分,就是x和x x + 1, 如果有m种划分,就是 x 、x x + 1 、 x x + 1 x + 2 、... 、x x + 1 x + 2 ... x + m - 1
    将每一个结果相加得到一个公式(i * x + i * (i - 1) / 2) = n,i为当前划分后相加的正整数个数。
    满足条件的划分就是使x为正整数的所有情况。
    如上例,当i = 1时,即划分成一个正整数时,x = 15, 当i = 2时, x = 7。
    当x = 3时,x = 4, 当x = 4时,4/9,不是正整数,因此,15不可能划分成4个正整数相加。
    当x = 5时,x = 1。

        这里还有一个问题,这个i的最大值是多少?不过有一点可以肯定,它一定比n小。我们可以做一个假设,
    假设n可以拆成最小值为1的划分,如上例中的1 2 3 4 5。这是n的最大数目的划分。如果不满足这个假设,
    那么 i 一定比这个划分中的正整数个数小。因此可以得到这样一个公式i * (i + 1) / 2 <= n,即当i满足
    这个公式时n才可能被划分。

    代码如下:

    void split(int n) {
        int i, j, te, x, xlen;
        for (i = 1, xlen = 0; (te = i * (i - 1) / 2) < n; i++) {
            x = n - te;
            if (x % i == 0) {
                x /= i;
                printf("%d", x);
                for (j = 1; j < i; j++) {
                    printf("%d ", x + j);
                }
                printf("
    ");
                xlen++;
            }
        }
        printf("%d
    ", xlen);
    }
    View Code

    以下是转载的:

      求划分因子乘积最大的一个划分及此乘积
      问题简述:给定一个正整数n, 则在n所有的划分中, 求因子乘积最大的一个划分及此乘积。例如:8 = {8}, {7, 1}, {6, 2}, {5, 3}, {4, 4}, {3, 3, 2}, {2, 2, 2, 2} 等,那么在这些当中,3 * 3 * 2 的乘积最大,所以输出整个划分
    和这个乘积 18。
      算法分析:这是我在某个论坛上看到的问题,以及别人针对此问题的数学分析,现简单的整理如下:
      (1)对于任意大于等于4的正整数m, 存在一个划分m = m1+m2, 使 m1*m2 >= m证: 令m1 = int(m/2), 则 m1 >= 2 , m2 = m-m1; 那么m2 > 2,并且 m2 >= m/2 >= m1;    m1*m2 >= 2*m2 >= m; 证毕;
    该证明简单的来说就是:对于一个大于等于4的正整数m,存在一个2块划分的因子,这两个因子的乘积总是不小于原数m本身。
      (2)由(1)知此数最终可以分解为 2^r * 3^s。现证明 r <= 2;
      证:若r > 2, 则至少有3个因子为2, 而2*2*2 < 3*3;
      所以可以将3个为2的因子,换为两个因子3;积更大;证毕。
      综合(1),(2),则有:任何大于4的因子都可以有更好的分解, 而4可以分解为2*2。
      所以:此数应该分解为 2^k1 * 3^k2。而且可以证明 k1>=0 并且 k1 <= 2,因此:
         A.当n = 3*r 时, 分解为 3^r
         B.当n = 3*r+1时, 分解为 3^(r-1)*2*2
         C.当n = 3*r+2时, 分解为 3^r*2
      剩下编程处理,那就是太简单了,首先是处理 <= 4的特殊情况,再对>4的情况进行模3的3种情况的判断,最后一一输出。可见,数学在整数划分问题上有太强的功能。谁叫这个问题叫整数划分呢,不与数学密切才怪! ^_^。

     

      小学六年级奥数---整数划分(有用结论)

      例1:把14分拆成若干个自然数的和,再求出这些数的积,要使得到的积最大,应该把14如何分拆?这个最大的乘积是多少?


      分析与解:我们先考虑分成哪些数时乘积才能尽可能地大。
      首先,分成的数中不能有1,这是显然的。
      其次,分成的数中不能有大于4的数,否则可以将这个数再分拆成2与另外一个数的和,这两个数的乘积一定比原数大,例如7就比它分拆成的2和5的乘积小。
      再次,因为4=2×2,故我们可以只考虑将数分拆成2和3。
      注意到2+2+2=6,2×2×2=8;3+3=6,3×3=9,因此分成的数中若有三个2,则不如换成两个3,换句话说,分成的数中至多只能有两个2,其余都是3。根据上面的讨论,我们应该把14分拆成四个3与一个2之和,即14=3+3+3+3+2,这五数的积有最大值 3×3×3×3×2=162。
      将上述结论推广为一般情形便是:
      把自然数S(S>1)分拆为若干个自然数的和:   S=a1+a2+…+an,则当a1,a2,…,an中至多有两个2,其余都是3时,其连乘积m=a1a2…an有最大值。

      例2:把1993分拆成若干个互不相等的自然数的和,且使这些自然数的乘积最大,该乘积是多少?
    解:由于把1993分拆成若干个互不相等的自然数的和的分法只有有限种,因而一定存在一种分法,使得这些自然数的乘积最大。
      若1作因数,则显然乘积不会最大。把1993分拆成若干个互不相等的自然数的和,因数个数越多,乘积越大。为了使因数个数尽可能地多,我们把1993分成2+3…+n直到和大于等于1993。
    若和比1993大1,则因数个数至少减少1个,为了使乘积最大,应去掉最小的2,并将最后一个数(最大)加上1。
    若和比1993大k(k≠1),则去掉等于k的那个数,便可使乘积最大。
    所以n=63。因为2015-1993=22,所以应去掉22,把1993分成(2+3+…+21)+(23+24+…+63)

    这一形式时,这些数的乘积最大,其积为  2×3×…×21×23×24×…×63。

     

    描述

    整数划分是一个经典的问题。请写一个程序,完成以下要求。

     
    输入
    每组输入是两个整数n和k。(1 <= n <= 50, 1 <= k <= n)
    输出
    对于输入的 n,k;
    第一行: 将n划分成若干正整数之和的划分数。
    第二行: 将n划分成k个正整数之和的划分数。
    第三行: 将n划分成最大数不超过k的划分数。
    第四行: 将n划分成若干个 奇正整数之和的划分数。
    第五行: 将n划分成若干不同整数之和的划分数。
    第六行: 打印一个空行
    样例输入
    5 2
    样例输出
    7
    2
    3
    3
    3
    
    提示
    样例输出提示:
    1.将5划分成若干正整数之和的划分为: 5, 4+1, 3+2, 3+1+1, 2+2+1, 2+1+1+1, 1+1+1+1+1
    2.将5划分成2个正整数之和的划分为: 3+2, 4+1
    3.将5划分成最大数不超过2的划分为: 1+1+1+1+1, 1+1+1+2, 1+2+2
    4.将5划分成若干 奇正整数之和的划分为: 5, 1+1+3, 1+1+1+1+1
    5.将5划分成若干不同整数之和的划分为: 5, 1+4, 2+3
    来源
    /*对于输入的 n,k;  
    第一行: 将n划分成若干正整数之和的划分数。  
    第二行: 将n划分成k个正整数之和的划分数。  
    第三行: 将n划分成最大数不超过k的划分数。  
    第四行: 将n划分成若干个 奇正整数之和的划分数。  
    第五行: 将n划分成若干不同整数之和的划分数。  
    第六行: 打印一个空行*/    
    #include<iostream>    
    #include <cstring>    
    #include<vector>    
    #include <cstdio>    
    using namespace std;    
    #define N 55+1    
    int dp[N][N];    
        
    int main()    
    {    
        //分为若干个正整数和    
        //memset(dp,0,sizeof(dp));    
        int n,k;    
        int out[6];    
        while(cin>>n>>k)    
        {    
            memset(dp,0,sizeof(dp));    
            //任意个正整数和,则dp[i][j]表示i分解成最大不超过j的个数,    
            //分为最大是j和最大不是j,则dp[i][j]=dp[i-j][j]+dp[i][j-1];    
            dp[0][0]=1;    
            for (int i=0;i<=n;i++)    
            {    
                for (int j=1;j<=n;j++)    
                {    
                    if(j<=i)    
                    dp[i][j]=dp[i-j][j]+dp[i][j-1];    
                    else    
                    dp[i][j]=dp[i][i];    
                }    
            }    
            out[1]=dp[n][n];    
            out[3]=dp[n][k];    
            //分成K个正整数的和 ,分为k个数中没有1,和有1,    
            //dp[i][j],将i划分为j个dp[i][j]=dp[i-j][j]+dp[i-1][j-1];    
            memset(dp,0,sizeof(dp));    
            for(int i=1;i<=n;i++)    
            for (int j=1;j<=i;j++)    
            {    
                  if(j==1)    
                  dp[i][j]=1;    
                  else    
                  dp[i][j]=dp[i-j][j]+dp[i-1][j-1];     
            }    
            out[2]=dp[n][k];    
           //奇数和dp[i][j]为划分最大的奇数不超过j的数,    
           //则dp[i][j]=dp[i-n(j)][j]+dp[i][j-2];n(j)为不超过j的最大奇数    
           //初始条件,dp[i][1]=1,j为偶数时候dp[i][j]=dp[i][j-1];当i==n(j) ,    
           //出现dp[0][j],也就是当i为奇数时候,dp[0][j]=1;    
           memset(dp,0,sizeof(dp));    
           for(int i=0;i<=n;i++)    
           {    
               dp[i][1]=1;    
               if(i&1)    
               dp[0][i]=1;    
            }    
            for (int i=1;i<=n;i++)    
            {    
                for (int j=1;j<=n;j++)    
                {    
                    if(j&1)    
                    {    
                        if(j<=i)    
                        dp[i][j]=dp[i-j][j]+dp[i][j-1];    
                        else     
                        dp[i][j]=dp[i][i];    
                    }    
                    else    
                    dp[i][j]=dp[i][j-1];    
                }    
            }    
            out[4]=dp[n][n];    
    //不同正整数和,dp[i][j]是不超过j的不同的整数和,dp[i][j]=dp[i-j][j-1]+dp[i][j-1];初始状态dp[1][1]=1;    
    //当i==j时,出现dp[0][j-1],表示先拿出一个j出来,这时候就应该是1中情况。    
        
            memset(dp,0,sizeof(dp));    
            dp[0][0]=1;    
            for (int i=0;i<=n;i++)    
            {    
                  for (int j=1;j<=n;j++)    
                  {    
                       if(j<=i)    
                       dp[i][j]=dp[i-j][j-1]+dp[i][j-1];    
                        else    
                        dp[i][j]=dp[i][i];    
                  }    
            }    
            out[5]=dp[n][n];    
            for (int i=1;i<=5;i++)    
                {    
                    cout<<out[i]<<endl;    
                }    
                cout<<endl;    
        
           }    
    } 
    View Code
  • 相关阅读:
    TensorFlow实现LeNet5模型
    jmeter+influxdb+grafana性能测试可视化报告
    jmeter命令执行脚本
    jmeter JSON Extractor使用
    jmeter上传文件
    jenkins登录信息无效,忘记密码
    XML
    JMeter函数和变量
    jmeter配置CSV Data Set Config
    jmeter发送Query String Parameters格式参数报错
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5349017.html
Copyright © 2011-2022 走看看