• 整数划分总结


    整数划分问题:
    笼统上说就是将一根整数划分成若干个整数之和的方案数。整数划分很多不同的问法,也有隐晦的问法。比如n个苹果放到m个盘子里,比如n个砖块堆成m个层阶梯。关于整数划分,大概有以下这么多扩展的问题:
    1,整数n划分成若干整数之和的方案数;
    2,整数n划分成k个整数之和的方案数;
    3,整数n划分成最大数不超过k的若干整数之和的方案数;
    4,整数n划分成最小数不低于k的若干整数之和的方案数;
    5,整数n划分成若干不同的整数之和的方案数;
    6,整数n划分成k个不同整数之和的方案数;
    7,整数n划分成最大数不超过k的若干不同整数之和的方案数;
    8,整数n划分成最小数不低于k的若干不同整数之和的方案数;
    9,整数n划分成若干奇数的方案数;
    10,整数n划分成若干偶数的方案数;

    这么多问题一个一个解决,其实解决其中一个问题,相关联的也迎刃而解。首先看第二个问题,为什么不看第一个问题呢?很简单仔细思考一下,枚举所有的k不就是第一个问题的解嘛。第二个问题的状态转移方程式 引入dp2[][]表示整数i分成j个整数的方案数

            memset(dp2,0,sizeof(dp2));
            dp2[0][0]=1;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=i;j++)
                {
                    dp2[i][j]=dp2[i-1][j-1]+dp2[i-j][j];
                }
            }
    

    怎么去解释这个状态转移方程呢?你可以把整数i分成j个整数之和看成i个苹果放到j个盘子里,不允许有空盘子。j个盘子,可以分成两种状态,所有盘子里最小的苹果数等于1,和大于1。等于1的情况,把这个最小苹果数的盘子拿掉,dp[i][j]=dp[i-1][j-1];若最小数大于1,那么把所有j的盘子都拿去一个苹果,dp[i][j]=dp[i-j][j]。那么第一个问题就也解决了。

    现在看第三个问题,最大数不超过k的划分数

            memset(dp3,0,sizeof(dp3));
            dp3[0][0]=1;
            for(int i=0;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(i>=j)
                        dp3[i][j]=dp3[i][j-1]+dp3[i-j][j];
                    else
                        dp3[i][j]=dp3[i][j-1];
                }
            }
    

    解释这个状态转移方程:dp[i][j]表示i分成最大数不超过j的划分数。dp[i][j]可以分成以下两类:
    1.最大数等于j;
    2.最大数小于j;
    对于第二种情况,满足划分条件的是dp[i][j-1],表示数i分成最大数不超过j-1的划分数,那就是数i分成最大数小于j的划分数。第一种情况,满足划分条件的是dp[i-j][j];dp[i-j][j]表示数i-j分成最大数不超过j的划分数,然后将‘+j’附加在每一个划分后面,就得到数分成最大数等于j的划分数。那么dp[i-j][j]的划分数就等于dp[i][j]的划分。
    那么看一到例题吧
    POJ 1664 放苹果
    其实,放苹果问题,因为可以允许有空盘子,一个盘子里的苹果最多不超过k个盘子数,所以这个问题就可以转换成数i分成最大数不超过k的划分数
    贴一下AC代码吧

    #include <iostream>
    #include <algorithm>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    #include <stdio.h>
    
    using namespace std;
    int dp[15][15];
    int n,m;
    void fun()
    {
    
        for(int i=1;i<=11;i++)
        {
            dp[i][1]=1;
            //dp[i][0]=0;
           for(int j=1;j<=11;j++)
           {
               dp[0][j]=1;
               if(i<j)
                dp[i][j]=dp[i][j-1];
               else
                dp[i][j]=dp[i][j-1]+dp[i-j][j];
           }
        }
    }
    int main()
    {
        memset(dp,0,sizeof(dp));
        fun();
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
    
            printf("%d
    ",dp[n][m]);
        }
    }

    第三个问题解决之后,也可以解决第一个问题了,就是把最大数不超过k的k从1到n枚举一下就可以了。

    第四个问题和第三个问题很相似,
    先看状态转移方程

                    memset(dp4,0,sizeof(dp4));
                    for(int i=0;i<=n;i++)
                        dp4[0][i]=1;
                    for(int i=1;i<=n;i++)
                    {
                        for(int j=n;j>=1;j--)
                        {
                            if(i>=j)
                                dp4[i][j]=dp4[i][j+1]+dp4[i-j][j];
                            else
                                dp4[i][j]=dp4[i][j+1];
                        }
                    }
    

    同样的,dp[i][j]可以分成两大类
    1.最小数等于j;
    2.最小数大于j;
    满足第二个情况的划分条件是dp[i][j+1],表示数i分成最小数大于j的情况。满足第一个情况的划分条件是dp[i-j][j],表示i-j的最小数不小于j的划分,然后将‘+j’附加在每一个划分后面,就得到数i分成最小数等于j的划分。同理枚举k可以得出第一个问题的解

    第五个问题,到这里可以看出前四个是一个类型,后面四个是另一个类型,后面多出一个要求,就是要求不同的数字。同理第五个问题是由第六个,七个,八个问题求解出来。
    那么直接看第六个问题,这里我们和前面的联系起来看,因为只多了一个数字不同的条件
    先看状态转移方程

                    memset(dp6,0,sizeof(dp6));
                    dp6[0][0]=1;
                    for(int i=1;i<=n;i++)
                    {
                        for(int j=i;j>=1;j--)
                        {
                            dp6[i][j]=dp6[i-j][j]+dp6[i-j][j-1];
                        }
                    }
    

    可以看出和第二个问题状态转移方程的区别就是一点,dp[i][j-1]变成dp[i-j][j-1]。这里我们不用放苹果,我们用i个砖块堆成有j个阶次的阶梯,阶梯要求不能有相邻两个是同样的高度
    这里写图片描述
    状体转移方程可以描述为,把阶梯最底层的一层砖块全部拿掉,阶梯要么还是不变的阶数,要么阶数少了一阶。
    所以dp[i][j]=dp[i-j][j]+dp[i-j][j-1];
    第五个问题直接枚举j就好了。
    这里有一道例题,就是明白着的问题六
    HOJ 1090

    第七个问题,直接看状态转移方程

                memset(dp7,0,sizeof(dp7));
                    dp7[0][0]=1;
                    for(int i=0;i<=n;i++)
                    {
                        for(int j=1;j<=n;j++)
                        {
                            if(i>=j)
                                dp7[i][j]=dp7[i][j-1]+dp7[i-j][j-1];
                            else
                                dp7[i][j]=dp7[i][j-1];
                        }
                    }
    

    和问题四的区别就是dp[i-j][j]变成了dp[i-j][j-1]
    同样的问题分成两类,第二类是不变的dp[i][j-1],第一类变成了dp[i-j][j-1],对于第一类,dp[i-j][j-1]表数i-j分成最大数不超过j-1的划分数,也就是i-j分成最大数小于j,将‘+j’附加在每个划分后面,得到数i分成最大数等于j的划分。这样就避免了有重复的,因为dp[i-j][j]表示最大数不超过j,就有等于j的情况,那么再加上j就有重复的情况了。

    第八个问题,就应该很好理解了
    附代码

    memset(dp8,0,sizeof(dp8));
            for(int i=0;i<=n+1;i++)
                dp8[0][i]=1;
            for(int i=1;i<=n;i++)
            {
                for(int j=n;j>=1;j--)
                {
                    if(i>=j)
                        dp8[i][j]=dp8[i][j+1]+dp8[i-j][j+1];
                    else
                        dp8[i][j]=dp8[i][j+1];
                }
            }
    

    第九问题,分成若干个奇数
    只需要判断一下奇偶性就好了
    代码

          memset(dp9,0,sizeof(dp9));
            dp9[0][0]=1;
            for(int i=0;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(i>=j)
                    {
                        if(j%2==1)
                            dp9[i][j]=dp9[i][j-1]+dp9[i-j][j];
                        else
                            dp9[i][j]=dp9[i][j-1];
                    }
                    else
                        dp9[i][j]=dp9[i][j-1];
                }
            }
    

    最后一个,就不用说了。

    写到这里,就应该差不多了。感谢老王对我说的一句话:
    “我觉得潜心研究一个东西就能有显著提高”
    所以,我想说,搞ACM,刷题数量并不是最重要的,不要去关注Total Accept。去反复研究一个问题,它带给你的,有时候比刷二十道,三十道,还要多,还要有用。

  • 相关阅读:
    12:00的死亡游戏
    数字图像的5种增强处理
    评分系统
    学生信息的检索
    读心球游戏
    图像的中值滤波处理
    山西省高考成绩的排次系统【可查询成绩改正和将作弊成绩去除】
    打印总分与各科成绩单
    各城市地形图的分幅与编号查询系统
    在Visual Studio中利用NTVS创建Pomelo项目
  • 原文地址:https://www.cnblogs.com/dacc123/p/8228801.html
Copyright © 2020-2023  润新知