URAL 1017. Staircases 解题报告

URAL      1017. Staircases       解题报告



给n块砖头,文能组成多少个楼梯,楼梯是有要求的,至少两层,层与层之间数目不能相等!
和划分数极其类似,
将n分成几个数相加的形式,而且还不能有重复的数字……
具体讲解 我看了这篇日志才明白!   以自己的能力现在还想不出来!
光这道题花了我半天的时间,搜过这种解题报告,终于搞懂了,心情特别舒服!!

题意

这道题目要求计算给定数目的砖块可以组成多少种不同的楼梯。楼梯由不同高度的阶梯组成,不允许有两个阶梯有相同的高度。每个楼梯至少包含两个阶梯,每个阶梯至少包含一个砖块。

数学背景

这道题目涉及到数的分划,属于组合数学数论的研究领域。

数 n 的分划(partition)是将 n 表示成任意多个正整数之和的形式。例如,数 5 的分划如下:

  1. 5
  2. 4 + 1
  3. 3 + 2
  4. 3 + 1 + 1
  5. 2 + 2 + 1
  6. 2 + 1 + 1 + 1
  7. 1 + 1 + 1 + 1 + 1

用 p(n) 来记 n 的分划的个数,这样就有 p(5) = 7。

为了求解 p(n),我们引入一个中间函数 p(k, n),表示数 n 的最小被加数不小于 k 的分划的个数。对于给定的 k 值,p(k, n) 正好分为以下两类:

  1. 最小被加数等于 k
  2. 最小被加数大于 k

满足第一个条件的分划的个数是 p(kn − k)。 这是因为,让我们想象数 n − k 的最小被加数不小于 k 的分划,然后将 "+ k" 附加每一个分划后面,就得到数 n 的最小被加数等于 k 的分划。以 n = 5, k = 1 为例,数 4 的最小被加数不小于 1 的分划是 43 + 12 + 22 + 1 + 1 和 1 + 1 + 1 + 1,即 p(kn − k) = p(1, 4) = 5。然后,将 "+ 1" 附加在这 5 个分划后面,就得到数 5 的最小被加数等于 1 的分划:4 + 13 + 1 + 12 + 2 + 12 + 1 + 1 + 1 和 1 + 1 + 1 + 1 + 1

满足第二个条件的分划的个数是 p(k + 1, n) 。以 n = 5, k = 1 为例,数 5 的最小被加数大于 1 的分划是 5 和 3 + 2,即 p(k + 1, n) = p(2, 5) = 2。

也就是说,p(1, 5) = p(2, 5) + p(1, 4)。因此:

  • p(kn) = 0  如果 k > n
  • p(kn) = 1  如果 k = n
  • p(kn) = p(k+1, n) + p(kn-k 其它情况

这样,就可以递归地求解 p(k, n),其部分值见下表:

  k
1 2 3 4 5 6 7 8 9 10
n 1 1 0 0 0 0 0 0 0 0 0
2 2 1 0 0 0 0 0 0 0 0
3 3 1 1 0 0 0 0 0 0 0
4 5 2 1 1 0 0 0 0 0 0
5 7 2 1 1 1 0 0 0 0 0
6 11 4 2 1 1 1 0 0 0 0
7 15 4 2 1 1 1 1 0 0 0
8 22 7 3 2 1 1 1 1 0 0
9 30 8 4 2 1 1 1 1 1 0
10 42 12 5 3 2 1 1 1 1 1

最后,p(n) = p(1, n)

 


现在,让我们的来考虑 将 n 分成不相等的正整数之和的分划。例如,数 8 的分划如下:

  1. 8
  2. 7 + 1
  3. 6 + 2
  4. 5 + 3
  5. 5 + 2 + 1
  6. 4 + 3 + 1

用 q(n) 来记 n 的分划的个数,这样就有 q(8) = 6。

为了求解 q(n),我们引入一个中间函数 q(k, n),表示数 n 的最小被加数不小于 k 的分划的个数。对于给定的 k 值,q(k, n) 正好分为以下两类:

  1. 最小被加数等于 k
  2. 最小被加数大于 k

满足第一个条件的分划的个数是 q(k + 1, n − k)。 这是因为,让我们想象数 n − k 的最小被加数大于 k 的分划,然后将 "+ k" 附加每一个分划后面,就得到数 n 的最小被加数等于 k 的分划。以 n = 8, k = 1 为例,数 7 的最小被加数大于 1 的分划是 75 + 2 和 4 + 3,即 q(k + 1, n − k) = q(2, 7) = 3。然后,将 "+ 1" 附加在这 3 个分划后面,就得到数 8 的最小被加数等于 1 的分划:7 + 15 + 2 + 1 和 4 + 3 + 1

满足第二个条件的分划的个数是 q(k + 1, n) 。以 n = 8, k = 1 为例,数 8 的最小被加数大于 1 的分划是 86 + 2 和 5 + 3,即 q(k + 1, n) = q(2, 8) = 3。

也就是说,q(1, 8) = q(2, 8) + q(2, 7)。因此:

  • q(kn) = 0  如果 k > n
  • q(kn) = 1  如果 k = n
  • q(kn) = q(k+1, n) + q(k + 1, n-k)  其它情况

这样,就可以递归地求解 q(k, n),其部分值见下表:

  k
1 2 3 4 5 6 7 8 9 10
n 1 1 0 0 0 0 0 0 0 0 0
2 1 1 0 0 0 0 0 0 0 0
3 2 1 1 0 0 0 0 0 0 0
4 2 1 1 1 0 0 0 0 0 0
5 3 2 1 1 1 0 0 0 0 0
6 4 2 1 1 1 1 0 0 0 0
7 5 3 2 1 1 1 1 0 0 0
8 6 3 2 1 1 1 1 1 0 0
9 8 5 3 2 1 1 1 1 1 0
10 10 5 3 2 1 1 1 1 1 1

最后,q(n) = q(1, n)

解题思路

用 n 块砖块可以组成的楼梯的个数,就是将 n 分成不相等的正整数之和的分划的个数 q(n) 减一,因为每个楼梯至少包含两个阶梯。

相应的 C# 程序如下: 

复制代码
 1  namespace  Skyiv.Ben.Timus
 2  {
 3     //   http://acm.timus.ru/problem.aspx?space=1 &num=1017
 4     sealed   class  T1017
 5    {
 6       static   void  Main()
 7      {
 8        var m  =   int .Parse(System.Console.ReadLine());
 9        var q  =   new   long [m  +   1 , m  +   1 ];
10         for  (var k  =   1 ; k  <=  m; k ++ ) q[k, k]  =   1 ;
11         for  (var n  =   2 ; n  <=  m; n ++ )
12           for  (var k  =  n  -   1 ; k  >=   1 ; k -- )
13            q[k, n]  =  q[k  +   1 , n]  +  q[k  +   1 , n  -  k];
14        System.Console.WriteLine(q[ 1 , m]  -   1 );
15      }
16    }
17  }
复制代码

上述程序中的二维数组 q 的下标 0 没有使用,因此浪费了 (2m + 1) * sizeof(long) 字节的空间。因此将 k 和 n 的取值范围从 1 到 m 修改为 0 到 m-1,则得到以下等价的程序:

复制代码
 1  namespace  Skyiv.Ben.Timus
 2  {
 3     //   http://acm.timus.ru/problem.aspx?space=1 &num=1017
 4     sealed   class  T1017
 5    {
 6       static   void  Main()
 7      {
 8        var m  =   int .Parse(System.Console.ReadLine());
 9        var q  =   new   long [m, m];
10         for  (var k  =   0 ; k  <  m; k ++ ) q[k, k]  =   1 ;
11         for  (var n  =   1 ; n  <  m; n ++ )
12           for  (var k  =  n  -   1 ; k  >=   0 ; k -- )
13            q[k, n]  =  q[k  +   1 , n]  +  q[k  +   1 , n  -  k  -   1 ];
14        System.Console.WriteLine(q[ 0 , m  -   1 -   1 );
15      }
16    }
17  }
复制代码

注意第 13 行的公式中最后一个下标从 n - k 变为 n - k - 1。这是因为该公式位于 n 和 k 的二重循环中,这两个循环变量的取值范围都已经减一了,n - k 的值抵消了减一,所以也需要再减一。

进一步的阅读

首先,推荐维基百科网站的如下页面:Partition (number theory)

其次,推荐以下书籍:

组合数学(原书第4版),(美) Richard A.Brualdi 著,冯舜玺等译,机械工业出版社,2005年2月第1版。该书第八章“特殊计数序列”,第三节“8.3 分拆数”。

数论导引(原书第5版),(英) G.H.Hardy, E.M.Wright 著,张明尧等译,人民邮电出版社,2008年10月第1版。该书第十九章“分划”。

关于数的分划,有许多有趣的定理,如:

定理 344  将 n 分成若干个不相等的数之和的分划个数等于将它分成若干个奇数之和的分划个数。

这个定理是伟大的数学家欧拉(Leonard Euler)在1748年首先证明的。







下面是我A的代码:
状态转移方程弄出来了,但是还得自己实现,转移的时候得确定一个最初的状态,这里的最初的状态就是f[i][i];      
有了最初的状态之后才能转移,否则再好的状态,再好的状态转移方程都白搭啊!  然后还得看看转移的顺序,就是上边依靠下边的话,下边得先算,就是被依靠着,被转移者得先处理啊!  明白了这两点之后就很好code了!

#include<iostream>
#include<cstring>
#define N 500
using namespace std;
long long f[N+1][N+1];
void init()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=N;++i) f[i][i]=1;///降序升序

    for (long i=N;i>=1;i--)///这两个循环为什么降序,为什么升序要根据状态转移方程来算
    {///我测试过两个循环谁嵌套谁都没事,但是升序降序必须搞清楚
        for (long j=i+1;j<=N;j++)
            f[i][j]=f[i+1][j]+f[i+1][j-i];
    }
}
int main()
{
    long n;
    init();
    while(cin>>n)
       cout << f[1][n]-1 << endl;///注意这里的输出形式

    return 0;
}






1017. Staircases

Time limit: 1.0 second
Memory limit: 64 MB
One curious child has a set of  N little bricks (5 ≤  N ≤ 500). From these bricks he builds different staircases. Staircase consists of steps of different sizes in a strictly descending order. It is not allowed for staircase to have steps equal sizes. Every staircase consists of at least two steps and each step contains at least one brick. Picture gives examples of staircase for  N=11 and N=5:
Problem illustration
Your task is to write a program that reads the number  N and writes the only number  Q — amount of different staircases that can be built from exactly  N bricks.

Input

Number  N

Output

Number  Q

Sample

input output
212
995645335
Problem Source: Ural State University Internal Contest '99 #2 


URAL      1017. Staircases       解题报告

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值