题目链接: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的方程是
然后依次类推,然后你每项算出来显然是n^3显然会T掉吧,又观察这些式子是有规律,每一项都和dp[i-1][j-1]有关,这就让我们可以用前缀和就好了。
这就有了代码的关键式子:
//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;
}