• 【noi 2.6_8787】数的划分(DP){附【转】整数划分的解题方法}


    题意:问把整数N分成K份的分法数。(与“放苹果”不同,在这题不可以有一份为空,但可以类比)
    解法:f[i][j]表示把i分成j份的方案数。
    f[i][j]=f[i-1][j-1](新开一份,放1)
    而i≥j时,f[i][j]=f[i-1][j-1]  +f[i-j][j](不新开一份时的方案数与每份中都少放1的方案数相同)

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

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    using namespace std;
    
    int f[210][10];
    int main()
    {
        int n,k;    
        scanf("%d%d",&n,&k);
        memset(f,0,sizeof(f));
        for (int i=1;i<=n;i++) f[i][1]=1;
        for (int i=1;i<=n;i++)
          for (int j=2;j<=k;j++)
            if (j<=i)
              f[i][j]=f[i-1][j-1]+f[i-j][j];
        printf("%d",f[n][k]);
        return 0;
    }
    View Code

    附——整数划分的解题方法

    {Article 1 转自——http://blog.csdn.net/luke2834/article/details/49429353 ;                                                   Article 2 转自——http://blog.csdn.net/athenaer/article/details/8265234}

    其中一些问题具体实现见我另外几篇博文——http://www.cnblogs.com/konjak/p/6004018.html & http://www.cnblogs.com/konjak/p/6004636.html

    我略修改了一下原博文,大家可比较一下。Article 1的方法很特别,Article 2的思路常规但也挺重要的。

    问题描述给定一个正整数N和K
    1. 将n划分成若干正整数之和的划分数。
    2.将n划分成k个正整数之和的划分数。
    3.将n划分成最大数不超过k的划分数。
    4.将n划分成若干奇正整数之和的划分数。
    5.将n划分成若干不同整数之和的划分数。
    Article 1:总的来说这些都是背包问题;
    第一个问,就是一个完全背包,背包有 1 --- N 种,第 i 种背包的重量为 i ,价值为 i ;这很好解决:

    1 dp[0] = 1;
    2 for (i = 1;i <= N;i++)
    3   for (j = i;j <= N;j++)
    4     dp[j] += dp[j-i];

    其中 dp[j] 是用前 i 个数能构成 j 的种类数,则结果就为 dp[N]
    看完这个问题了,那么 第3个问就知道了 , 即用前 K 种背包来装 所得结果,只需把第一层循环的 i <= N 改为 i <= K 即可;

    1 dp[0] = 1;
    2 for (i = 1;i <= K;i++)
    3   for (j = i;j <= N;j++)
    4     p[j] += dp[j-i];

    那么第四个问呢,想想是奇数,那么 i = 2,4,6,…… 等等值就不能取了,因为这些背包种类不合要求,这很简单啊  i++ 改为 i += 2 不就行了;

    1 dp[0] = 1;
    2 for (i = 1;i <= N;i+=2)
    3   for (j = i;j <= N;j++)
    4     dp[j] += dp[j-i];

    再看看第五个问,若干个不同的???想到了什么,就是一种背包最多只能用一次???这是什么,经典的 01背包 啊,与第一个问的不同就是第二层循环的顺序而已;

    1 dp[0] = 1;
    2 for (i = 1;i <= N;i++)
    3   for (j = n;j >= i ;j--)
    4     dp[j] += dp[j-i];

    最后我们来思考第二个问:要求只要 K 个,这怎么办呢???想想特殊情况…… 如果 K = 1 呢,只能是 N 咯,若果 N = 0 呢, 结果只能是 0 种可能啊,那同样N < K 的话,不可能分啊。结果为 0 ,好,特殊的考虑完了,那么我们再考虑,分的结果中有没有 1 。 如果有1,那么就把剩下的 N - 1 分成 K - 1 份 ; 如果没有呢,那么我们先拿出 K 份 给每一堆 一个1, 再把剩下的 N - K 分成 K 份就行了。

    Article 2:整数划分 --- 一个老生长谈的问题:     

    1.将n划分成不大于m的划分法: P.S.依问题定义——不定义划分成多少份。

       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];
             方法可以分为两类:
           第一类:i中拿出j个1分到每一份中,将剩余的i-j分成j个奇数
           第二类:一份包含奇数1,剩余的i-1分成j-1个奇数;另一种,每份至少大于1,将j个1拿出来分到每一份中,其余i-j分成j份

  • 相关阅读:
    Oracle 查询主外键关联
    maven
    LOG4J
    转换maven 项目为web 项目
    Iframe 高度自适应的问题
    JS 之CLASS类应用
    不要自己决定如何设计,遵从客户和客观需要
    私服搭建Nexus
    python经典算法题:无重复字符的最长子串
    Python输出hello world(各行命令详解)
  • 原文地址:https://www.cnblogs.com/konjak/p/5950919.html
Copyright © 2020-2023  润新知