题目描述
难度分:1500
输入T(≤104)表示T组数据。
每组数据输入n(1≤n≤4×104)。
输出把n拆分成若干回文数之和的方案数。结果可能很大,对结果模上109+7。
例如3有3种方案:3,2+1,1+1+1。
输入样例
2
5
12
输出样例
7
74
算法
完全背包求方案数
这个题可以先预处理出4×104范围内的所有回文数,存入a数组中。结果发现只有不到500个,那就可以利用完全背包来求方案数,相当于从a中选数,每个数的个数没有限制,使得刚好凑出数n的方案数。
边界条件是f[0]=1,表示0有一种拆分方案。外层循环遍历i∈[1,m],其中m表示[1,4×104]内回文数的个数。内层循环遍历j∈[a[i],n],使用完全背包优化掉第一维的技巧,状态转移方程为
f[j]=f[j]+f[j−a[i]]
最后的答案就是f[n]。
复杂度分析
时间复杂度
考虑到有多组测试用例,可以先在整个数据范围[0,4×104]上进行动态规划,时间复杂度为O(mn)。然后t个查询直接O(1)查表即可,整体的时间复杂度为O(mn+t)。
空间复杂度
空间瓶颈在于DP
数组f,它的规模是O(n)的,因此算法的额外空间复杂度为O(n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 40010, MOD = 1e9 + 7;
int t, n, f[N], a[500];
int rev(int x) {
int res = 0;
while(x) {
res = 10*res + x%10;
x /= 10;
}
return res;
}
int main() {
scanf("%d", &t);
int m = 0;
for(int i = 1; i <= N - 10; i++) {
f[i] = 0;
if(i == rev(i)) {
a[++m] = i;
}
}
f[0] = 1;
for(int i = 1; i <= m; i++) {
for(int j = a[i]; j <= N - 10; j++) {
f[j] = (f[j] + f[j - a[i]]) % MOD;
}
}
while(t--) {
scanf("%d", &n);
printf("%d\n", f[n]);
}
return 0;
}