传送门:今天不学习,明天变辣鸡
简述一下题意:给出一个n,求有多少回文数相加的方式等于n。
其中4 = 1+2+1 和 4 = 2+1+1为一种相加方式,要求方式间至少要有一个数字不同,才能叫做不同方式。
首先,n<4e4的,而且还是仅考虑范围中的回文数,所以实际的数据量是很小的,不会超过1e3,所以写个最暴力的dp也才4e7的计算量,给2s的时间绝对够了。
怎么判断回文数见代码实现。
然后就是如何设计递推公式和dp数组了。这个我也没想出来,直接看的tutorial。
记dp[k][m]表示前m个回文数相加为k的方式数。这里m虽然限定范围在前m个,但是并不是m个数字都要使用。
递推公式:其中pm为第m个回文数。
d p [ k ] [ m ] = d p [ k ] [ m − 1 ] + d p [ k − p m ] [ m ] dp[k][m] = dp[k][m-1] + dp[k-p_m][m] dp[k][m]=dp[k][m−1]+dp[k−pm][m]
前m个回文数构成k,可由两个状态推导,前m-1个数组成k的方式数+前m个回文数组成k-pm的方式数。不难看出,组成k的加数是有序的,所以不会出现重复的情况,也就是说只会有5=1+2+2,而不会出现5=2+1+2这种情况。
此外注意到递推式的最后一项,为什么是
d
p
[
k
−
p
m
]
[
m
]
dp[k-p_m][m]
dp[k−pm][m]而不是
d
p
[
k
−
p
m
]
[
m
−
1
]
dp[k-p_m][m-1]
dp[k−pm][m−1]呢?首先,题目中没有规定回文数只能使用一次,是可以多次使用的。如果换成
d
p
[
k
−
p
m
]
[
m
−
1
]
dp[k-p_m][m-1]
dp[k−pm][m−1],则表示加上pm后,从前m-1个回文数增加到前m个;忽略了加pm后,仍然是前m个不变的情况。
至于二层循环的执行顺序,只要保证dp[i][j]计算前,依赖项被完全计算即可。
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
template<class T> void _deb(const char *name,T val){
cout<<name<<val<<endl;
}
const int MAXN = 4e4+2;
const ll MOD = 1e9+7;
int t,n;
ll dp[MAXN][500];
vector<int> palindromes;
bool isPalindrome(int n);
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
// find all palindrome at range less than MAXN
rep(i,1,MAXN)
if(isPalindrome(i)){
palindromes.push_back(i);
}
rep(i,0,MAXN) {
dp[i][1] = 1;
dp[0][i] = 1;
}
int len = palindromes.size();
rep(i,1,len) {
int ind = i+1; // pos in dp
int num = palindromes[i];
rep(j,1,MAXN){
dp[j][ind] = (dp[j][ind-1] + ((j-num>=0)? dp[j-num][ind]:0)) % MOD;
}
}
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
printf("%d\n", dp[n][len]);
}
re 0;
}
bool isPalindrome(int n) {
int s = n;
int _reverse = 0;
while(s){
_reverse = _reverse*10 + (s%10);
s = s / 10;
}
return n==_reverse;
}