codeforce1673C. Palindrome Basis

本文介绍了一种动态规划方法来解决寻找所有回文数相加方式的问题,其中n小于4e4。博客详细解释了如何判断回文数,并给出了递推公式dp[k][m] = dp[k][m-1] + dp[k-p_m][m]。通过这个公式,可以避免重复计算并有效地计算出所有可能的加法组合。代码实现中包括了填充dp数组的逻辑,确保了正确性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:今天不学习,明天变辣鸡

简述一下题意:给出一个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][m1]+dp[kpm][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[kpm][m]而不是 d p [ k − p m ] [ m − 1 ] dp[k-p_m][m-1] dp[kpm][m1]呢?首先,题目中没有规定回文数只能使用一次,是可以多次使用的。如果换成 d p [ k − p m ] [ m − 1 ] dp[k-p_m][m-1] dp[kpm][m1],则表示加上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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值