• NYOJ-571 整数划分(三)


    此题是个非常经典的题目,这个题目包含了整数划分(一)和整数划分(二)的所有情形,而且还增加了其它的情形,主要是用递归或者说是递推式来解,只要找到了递推式剩下的任务就是找边界条件了,我觉得边界也是非常重要的一步,如果找不准边界,这个题也很难做出来,当时我就是找边界找了好长时间,边界得琢磨琢磨。递推步骤如下:

    第一行:将n划分成若干正整数之和的划分数。
    状态转移方程:dp[i][j]:和为i、最大数不超过j的拆分数
    dp[i][j]可以分为两种情况:1、拆分项至少有一个j 2、拆分项一个j也没有
    dp[i][j] = dp[i-j][[j] + dp[i][j-1]

    第二行:将n划分成k个正整数之和的划分数。
    dp[n-k][k]:相当于把k个1从n中拿出来,然后和n-k的拆分项相加的个数

    第三行:将n划分成若干最大不超过k的正整数之和的划分数。
    dp[n][k]

    第四行:将n划分成若干奇正整数之和的划分数。
    dp1[i][j]是当前的划分数为i,最大值为j时的中的划分数,则状态转移方程为
    if(i < j && j % 2 == 1)
    dp1[i][j] = dp1[i][i]
    if(i < j && j % 2 == 0) (最大数不可能为偶数)
    dp1[i][j] = dp1[i][i-1]
    划分数中有j时的划分为dp[i][j - 2],因为它是奇数,所以要减2,
    如果划分数中没有j的时候, 则它的数目可以写成dp1[i-j][j];意思就是i去掉j后,然后再划分最大为j的
    dp1[i][j] = dp1[i-j][j] + dp1[i][j-2]


    第五行:将n划分成若干完全不同正整数之和的划分数。
    dp2[i][j]可以分两种情况:1、dp1[i][j-1]为不选择j时的方案 2、dp1[i-j][j-1]为选择j时的方案
    0-1背包:dp2[i][j] = dp2[i][j-1] + dp2[i-j][j-1]

    方法一(递归法):

     1 #include <stdio.h>
     2 //less_m(n, m)表示将n划分为最大是m的数 
     3 int less_m(int n, int m)
     4 {
     5     if(n == 1 || m == 1)
     6         return 1;
     7     if(n < m)
     8         return less_m(n, n);
     9     else if(n == m)
    10         return 1 + less_m(n, m - 1);
    11     else
    12         return less_m(n, m - 1) + less_m(n - m, m);
    13 }
    14 //count(n, m)表示将n划分为m个数 
    15 int count(int n, int m)
    16 {
    17     if(n < m)
    18         return 0;
    19     if(m == 1 || n == m)
    20         return 1;
    21     else
    22         return count(n - 1, m - 1) + count(n - m, m);
    23 }
    24 //odd(n, m)表示将n划分为最大数为m的奇数之和 
    25 int odd(int n, int m)
    26 {
    27     if(m == 1)
    28         return 1;
    29     if(n == 0 && m % 2 == 1)
    30         return 1;
    31     if(n == 0 && m == 0)
    32         return 1;
    33     if(n < m)
    34     {
    35         if(n % 2 == 0)
    36             return odd(n, n - 1);
    37         else
    38             return odd(n, n);
    39     }
    40     else
    41     {
    42         return odd(n - m, m) + odd(n, m - 2);
    43     }
    44     
    45 }
    46 //not_duplicate(n, m)是将n划分为最大为m的数, 并且没有重复的 
    47 int not_duplicate(int n, int m)
    48 {
    49     if(m == 0)
    50         return 0;
    51     if(n == 1 || n == 0)
    52         return 1;
    53     if(n < m)
    54         return not_duplicate(n, n);
    55     else
    56         return not_duplicate(n, m - 1) + not_duplicate(n - m, m - 1);
    57 }
    58 
    59 int main()
    60 {
    61     int n, k;
    62     while(~scanf("%d %d", &n, &k))
    63     {
    64         printf("%d
    ", less_m(n, n));
    65         printf("%d
    ", count(n, k));
    66         printf("%d
    ", less_m(n, k));
    67         if(n % 2 == 1)
    68             printf("%d
    ", odd(n, n));
    69         else
    70             printf("%d
    ", odd(n, n - 1));
    71         printf("%d
    
    ", not_duplicate(n, n));
    72     }
    73 
    74     return 0;
    75 }
    View Code

    方法二(递推法dp):

     1 #include <stdio.h>
     2 const int MAX = 52;
     3 int dp[MAX][MAX], dp1[MAX][MAX], dp2[MAX][MAX];    
     4 void divide()
     5 {
     6     dp[0][0] = 1;
     7     for(int i = 0; i < MAX; i++)
     8     {
     9         for(int j = 1; j < MAX; j++)
    10         {
    11             if(i < j)
    12                 dp[i][j] = dp[i][i];
    13             else
    14                 dp[i][j] = dp[i][j - 1] + dp[i - j][j];
    15         }
    16     }
    17 }
    18 //这是划分奇数的函数 
    19 void divide1()
    20 {
    21     for(int i = 1; i < MAX; i++)
    22         dp1[i][1] = 1;
    23     for(int i = 1; i < MAX; i += 2)
    24         dp1[0][i] = 1;
    25     dp1[0][0] = 1;
    26     for(int i = 1; i < MAX; i++)
    27     {
    28         for(int j = 3; j < MAX; j += 2)
    29         {
    30             if(i < j)
    31             {
    32                 if(i % 2 == 1)
    33                     dp1[i][j] = dp1[i][i];
    34                 else
    35                     dp1[i][j] = dp1[i][i - 1];
    36             }
    37             else
    38                 dp1[i][j] = dp1[i][j - 2] + dp1[i - j][j];
    39         }
    40     }
    41 }
    42 //划分没有重复数字的函数 
    43 void divide2()
    44 {
    45     for(int i = 1; i < MAX; i++)
    46         dp2[0][i] = dp2[1][i] = 1;
    47     for(int i = 2; i < MAX; i++)
    48     {
    49         for(int j = 1; j < MAX; j++)
    50         {
    51             if(i < j)
    52                 dp2[i][j] = dp2[i][i];
    53             else
    54                 dp2[i][j] = dp2[i][j - 1] + dp2[i - j][j - 1];
    55         }
    56     }
    57 }
    58 
    59 int main()
    60 {
    61     int n, k;
    62     divide();
    63     divide1();
    64     divide2();
    65     while(~scanf("%d %d", &n, &k))
    66     {
    67         printf("%d
    ", dp[n][n]);
    68         printf("%d
    ", dp[n - k][k]);
    69         printf("%d
    ", dp[n][k]);
    70         //先要判断要划分的数是否是奇数 
    71         printf("%d
    ", (n & 1) ? dp1[n][n] : dp1[n][n - 1]);
    72         printf("%d
    
    ", dp2[n][n]);
    73     }
    74     
    75 }
    View Code
  • 相关阅读:
    CVPR2020:三维实例分割与目标检测
    CVPR2020:视觉导航的神经拓扑SLAM
    使用现代C++如何避免bugs(下)
    使用现代C++如何避免bugs(上)
    蓝牙mesh网络技术的亮点
    电路功能和优点
    ARM的突破:超级计算机和Mac
    所有处理都走向AI
    Wide-Bandgap宽禁带(WBG)器件(如GaN和SiC)市场将何去何从?
    功率半导体碳化硅(SiC)技术
  • 原文地址:https://www.cnblogs.com/Howe-Young/p/4066049.html
Copyright © 2020-2023  润新知