DP专题--数的划分

    NOIP之后感觉自己好像真的DP弱到不行,连背包都看不出来,= =其实也是因为并没有学习数学期望。所以滚过来专门补DP,还是从最基本的搞起来吧。
    改编自http://www.cnblogs.com/radiumlrb/p/5797168.html

题目要求:
1. 将n划分成若干正整数之和的划分数。
  2. 将n划分成k个正整数之和的划分数。
  3. 将n划分成最大数不超过k的划分数。
  4. 将n划分成若干奇正整数之和的划分数。
  5. 将n划分成若干不同整数之和的划分数。
  

  • 一.将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]

  • 二.将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]   
    另一种方式:
    dp[i,j]表示将i分成j份的方案数。
    dp[i,j]:=dp[i-j,1]+dp[i-j,2]+dp[i-j,3]+…+dp[i-j,j-1]+dp[i-j,j];
    时间复杂度是n*k^2。O(n*k)的方法:
    由于,
    dp[i,j]=dp[i-j,1]+dp[i-j,2]+…+dp[i-j,j];
    dp[i-1,j-1]=dp[(i-1)-(j-1),1]+dp[(i-1)-(j-1),2]+…+dp[(i-1)-(j-1),j-1]
    =dp[i-j,1]+dp[i-j,2]+…+dp[i-j,j-1];
    因此,
    dp[i,j]=dp[i-j,1]+dp[i-j,2]+…+dp[i-j,j-1]+dp[i-j,j]
    =dp[i-1,j-1]+dp[i-j,j];

    拓展:还有一个划分为不超过k组的问题
    就是看成与本题类似,但是可以有元素为0
    得到:dp[i,j]=dp[i-j,j]+dp[i,j-1]
    区别就是dp[i,j-1]中i没有-1
    参照hzwer的思想,因为元素可以为0,就算某一堆为空堆,总数也不需要-1

  • 三.将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, 则在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种情况的判断,最后一一输出。可见,数学在整数划分问题上有太强的功能。谁叫这个问题叫整数划分呢,不与数学密切才怪! ^_^。

  • 五.小学六年级奥数—整数划分(有用结论)
    把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。

你可能感兴趣的:(动态规划,dp)