题目链接:http://acm.uestc.edu.cn/#/problem/show/1307
题意:
在数电中,有一种码,类似BCD码这种玩意儿

第i位如果为1的话,那么ans+=a[i],a[i]是这一位的位权

然后现在给你一个n,问你一共有多少种码可以表示1~n的所有数呢?

1,1,2和2,1,1视作一样。

题外话:

去年有幸来到电子科技大学打了第一次ACM较为正规的比赛,现场第一次打了二等奖,但是没有解出来这个题。物是人非,再过一周又要去电大参加比赛了,熟悉的场景,但是队友却不同了。不知道今年能否实现目标,该加油了。

解法:
DP。
首先考虑这个东西,如果不视作一样的话,就很简单了

dp[i]表示当前和为i的方案数,显然这个玩意儿能够一直转移到2i-1去。(比如5,你转移到最大的肯定是3,不然就无法表示出1,2,3,4,5每个数了,这很显然)

由于视作一样,那么我们只要维护一个当前的最大值就好了,保证这个序列是递增的,这样就都不会一样了。

dp[i][j]表示现在和为i,最大值为j的方案数有多少。

转移:
朴素的n^3的方程是
dp[i+k][k]+=dp[i][j],kjn,i2i1

dp[0][0]=0
dp[1][1]=1
dp[2][1]=1
dp[3][1]=dp[2][1]
dp[3][2]=dp[2][1]
dp[4][2]=dp[3][1]
dp[5][2]=dp[4][1]+dp[3][2]
然后依次类推,然后你每项算出来显然是n^3显然会T掉吧,又观察这些式子是有规律,每一项都和dp[i-1][j-1]有关,这就让我们可以用前缀和就好了。
这就有了代码的关键式子:

dp[i][j]=(dp[i1][j1]+dp[ij][j])

//CDOJ 1307

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
long long dp[5010][5010];
long long ans[5010];

int main()
{
    dp[0][0] = 0;
    dp[1][1] = 1;
    dp[2][1] = 1;
    for(int i = 2; i <= 20; i++){
        dp[i][0] = 0;
        dp[i][1] = 1;
        for(int j = 1; j <= (i+1)/2; j++){
            dp[i][j] = (dp[i-1][j-1]+dp[i-j][j])%mod;
            ans[i] += dp[i][j];
            ans[i] %= mod;
            ///printf("dp[%d][%d] = %d ", i, j, dp[i][j]);
        }
        ///printf("\n");
    }
    ans[1] = 1;
    int T;
    scanf("%d", &T);
    while(T--){
        int n;
        scanf("%d", &n);
        printf("%lld\n", ans[n]);
    }
    return 0;
}