void-man

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

题目描述,给出一个整数,问你有多少种情况能把这个整数拆分成对称并且是双峰序列的和?

1: (1)
2: (2), (1 1)
3: (3), (1 1 1)
4: (4), (1 2 1), (2 2), (1 1 1 1)
5: (5), (1 3 1), (1 1 1 1 1)
6: (6), (1 4 1), (2 2 2), (1 1 2 1 1), (3 3),
(1 2 2 1), ( 1 1 1 1 1 1)
7: (7), (1 5 1), (2 3 2), (1 1 3 1 1), (1 1 1 1 1 1 1)
8: (8), (1 6 1), (2 4 2), (1 1 4 1 1), (1 2 2 2 1),
(1 1 1 2 1 1 1), ( 4 4), (1 3 3 1), (2 2 2 2),
(1 1 2 2 1 1), (1 1 1 1 1 1 1 1)

很显然要用dp,但是状态转移方程实在难找,忍不住百度了下答案,恍然大悟,dp[i][j]表示和为i的数最小数是j的要求序列的个数

然后dp[i][j]+=dp[i-2*j][k](k=j....i-*j),由于需要递增序列,所以把两端的最小数去掉后,每次求出一种i-2*j的序列,并且最小数

大于j,都可以在此两端分别加上j构成dp[i][j]的一个序列,由此得到这个转移方程

另外此题数据大,需要long long

#include "stdio.h"

#define MAXN 250

__int64 opt[MAXN][MAXN];

int n;

__int64 ans;

int main()

{

       int i,j,k;

       for (i = 0; i<MAXN; i++)

              opt[i][0] = opt[i][i] = 1;

       opt[1][1] = 1;

       opt[2][1] = 1;

       opt[2][2] = 1;

       for (i = 3; i<MAXN; i++)

              for (j = 1; j<=i/2; j++)

              {

                     if (i - 2 * j == 0)

                            opt[i][j]++;

                     else

                            for (k = j; k<=i - 2 * j; k++)

                                   opt[i][j] += opt[i - 2 * j][k];

              }

      

       while (scanf("%d",&n) != EOF && n)

       {

              ans = 0;

              for (i = 0; i<=n / 2; i++)

                     ans += opt[n][i];

              printf("%d %I64d\n",n,ans);

       }

       return 0;

}

posted on 2011-05-31 22:32  void-man  阅读(276)  评论(0)    收藏  举报