zoukankan      html  css  js  c++  java
  • [动态规划] 整数分解加数

     
    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Problem Description
    "Well, it seems the first problem is too easy. I will let you know how foolish you are later." feng5166 says.

    "The second problem is, given an positive integer N, we define an equation like this:
      N=a[1]+a[2]+a[3]+...+a[m];
      a[i]>0,1<=m<=N;
    My question is how many different equations you can find for a given N.
    For example, assume N is 4, we can find:
      4 = 4;
      4 = 3 + 1;
      4 = 2 + 2;
      4 = 2 + 1 + 1;
      4 = 1 + 1 + 1 + 1;
    so the result is 5 when N is 4. Note that "4 = 3 + 1" and "4 = 1 + 3" is the same in this problem. Now, you do it!"
     
    Input
    The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file.
     
    Output
    For each test case, you have to output a line contains an integer P which indicates the different equations you have found.
     
    Sample Input
    4
    10
    20
     
    Sample Output
    5
    42
    627
     


    问题翻译:
    给定一个正整数N,我们可以定义一个等式。
      N=a[1]+a[2]+a[3]+...+a[m];
      a[i]>0,1<=m<=N;
     
    问题:给定任意一个正整数N,能够找到多少个这种等式。
    以4为例。
      4 = 4;
      4 = 3 + 1;
      4 = 2 + 2;
      4 = 2 + 1 + 1;
      4 = 1 + 1 + 1 + 1;
    共有5个等式,注意"4=3+1"与"4=1+3"被定义为相同的等式。
     
    输入:
    每行一个正整数N(1<=N<=120),可能包含多行。
     
    输出:
    每行一个结果,表示可分解的等式个数。
     
    输入示例:
    4
    10
    20
     
    输出示例:
    5
    42
    627
     

     
    由于"4=1+3"和"4=3+1"是相同的等式,不妨约定等式中加数按递减顺序出现。
    手工分析5这个数,可以写成以下几个等式。
    5=5
    5=4+1
    5=3+2
    5=3+1+1
    5=2+2+1
    5=2+1+1+1
    5=1+1+1+1+1
     
    通过观察,可以发现这些等式可以按最大加数,分为几组。如最大加数为4的等式只有一个"5=4+1",最大加数为3的等式有2个,最大加数为2的等式也有2个。因此考虑将问题按最大加数的大小,分解为几个子问题。
    当最大加数为3时,所有的等式数量,相当于对2(5-3=2)分解加数。
    但是当最大加数为2时,所有等式的数量,不能按对3分解加数的值来计算。因为对3分解加数,最大加数可能大于2,这与之前约定的递减顺序冲突,会造成重复计算。例如会重复计算"5=2+3",这个等式在最大加数为3时已经计算过。所以,当最大加数为2时,子问题的解应该是对3分解加数,且最大加数不超过2的等式的数量。
     
    为了描述这个值,定义函数f(x,y)为对x分解加数,且最大加数不超过y的等式的数量。原问题的解是f(N,N),其中N为问题的输入。
    至此,可以写出递归公式。
    f(x,y) = ∑(i from 1 to y)( f(x-i, i) )
     
    当y>x时,f(x,y) = f(x,x)。从等式原始意义不难得出这个结论。
    其中,f(x,1) = 1,最大加数为1的等式只有一个,即x = 1 + 1 + 1 + ... + 1。为了形式上处理x = x + 0的等式,定义f(0, y) = 1。f(x,0)无意义。
     
    可以利用动态规划求解。以7为例模拟求解全过程为。表格横坐标代表f(x,y)中的x,纵坐标代表y。
      0 1 2 3 4 5 6 7
    1 1 1 1 1 1 1 1 1
    2 1   2 2 3 3 4 4
    3 1     3 4 5 7 8
    4 1       5 6 9 11
    5 1         7 10 13
    6 1           11 14
    7 1             15
     
     
     
     
     
     
     
     
     
     
     
    以f(7,7)为例(蓝色表格框),其值相当于对绿色框内所有数字求和。随着参数y从1开始增至x,绿色框从右上方向左下方延伸。
     

     
    代码示例:

    代码仅简单写出算法,并未完全按照题目的输入输出处理。代码输入的输入为N,输出为等式个数。
    如:
    > python my_alg.py 7
    15
     
     1 #!/usr/local/bin/python2.6
     2 
     3 import sys
     4 
     5 g_table = []
     6 
     7 def Init(num):
     8     global g_table 
     9     for i in range(0, num + 1):
    10         lst_num = []
    11         for i in range(0, num + 1):
    12             lst_num.append(-1)
    13         g_table.append(lst_num)
    14     for i in range(0, num + 1):
    15         g_table[i][1] = 1
    16         g_table[i][0] = 1
    17 
    18 def GetNumber(n, x):
    19     global g_table 
    20     if x > n:
    21         return g_table[n][n]
    22     return g_table[n][x]
    23 
    24 def PutNumber(n, x, num):
    25     global g_table 
    26     g_table[n][x] = num
    27 
    28 def Compute(n, x):
    29     sum = 0 
    30     for i in range(1, x + 1): 
    31         sum += GetNumber(n - i, i) 
    32     return sum     
    33 
    34 def Cal(n): 
    35     for i in range(2, n + 1): 
    36         for j in range(2, i + 1): 
    37             num = Compute(i, j)
    38             PutNumber(i, j, num) 
    39     return GetNumber(n, n)
    40 
    41 def main(): 
    42     n = int(sys.argv[1])
    43     Init(n) 
    44     print Cal(n)
    45 
    46 if __name__ == '__main__':
    47     main()  

     
    以前在算法课上学习动态规划,一知半解。后来自己读《算法导论》,也看了wiki上对与动态规划的介绍。动态规划的理论包括Bellman等式,还有状态转移函数等。记得老师在课堂上说用动态规划处理问题,要先写出状态转移函数。但是思考过一些问题,总感觉一开始就找状态转移函数,不太容易写出来。比如本问题、矩阵连乘问题、以及硬币找零问题的状态转义函数,该如何写呢。这几个问题并没有明显的stage顺序。
     
    反倒是感觉,应该先分解问题,寻找子问题,寻找递归式。动态规划的本质是记录子问题的结果,避免重复计算子问题。如果能找到问题的递归关系,就可以自底向上地计算。
     
    在实际中,问题的解可能并不直接依赖于相同形式的子问题。
    比如这道题,若定义f(N)为N分解加数的等式个数,则f(N)并不依赖于规模更小的f(N-1),f(N-2),....,f(3)等,而是依赖于有限制条件的等式个数,因此定义f(x, y)为最大加数为y的等式个数。这样f(N, N)就代表了不加限制的等式个数。
    再比如“能量苹果”那个问题,f(Lx)也不是只依赖于相同形式规模更小的子问题f(Lx-1),f(Lx-2),...,f(L3)等,还依赖于吃了偶数个苹果的情况。因此定义g(Lx)为吃了偶数个苹果时的最大能量,这时必须为g(Lx)也写出递归式,否则无法自底向上计算。
  • 相关阅读:
    Python编程第5讲—if 语句
    GIT 笔记
    jQuery多余文字折叠效果
    静态库与动态库的制作与使用
    Makefile
    C++ 有理数类
    使用mstest.exe 命令行跑test case(不安装Visual Studio 2010)
    Termp Folder and its subfolders
    ToString() 格式化字符串总结
    REST基础
  • 原文地址:https://www.cnblogs.com/terencezhou/p/2664949.html
Copyright © 2011-2022 走看看