大都会 J Beautiful Numbers (数位dp之数位和问题)

链接:https://www.nowcoder.com/acm/contest/163/J
来源:牛客网
 

题目描述

NIBGNAUK is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by the sum of its digits.

We will not argue with this and just count the quantity of beautiful numbers from 1 to N.

输入描述:

The first line of the input is T(1≤ T ≤ 100), which stands for the number of test cases you need to solve.
Each test case contains a line with a positive integer N (1 ≤ N ≤ 1012).

输出描述:

For each test case, print the case number and the quantity of beautiful numbers in [1, N].

 

示例1

输入

复制

2
10
18

输出

复制

Case 1: 10
Case 2: 12

【总结】

数位dp的模板题,但是状态转移方程怎么也没理解透,看完题解才恍然大悟。

通过此题,加深了对数位dp的理解。

【分析】

说白了就是dfs记忆化搜索,从高位向低位dfs,对整棵dfs树来说,有很多子树是一模一样的,因此记忆化剪枝。

对于一棵子树的状态,可以用祖先路径来记忆,当祖先路径上的数位和与余数一样时,由于子树都是一样的,所以这两个祖先路径分别衔接上这样的子树,对答案的贡献是相等的。

 

上面扯得可以忽略。

枚举每一个可能的位数和total,即1~9*pos

dp[i][j][r]:安排第i位时,前面i位的和为j,前i位构成的数字%total=r,该状态下,衔接上一个满的子树,对答案的贡献值。

【代码】

/****
***author: winter2121
****/
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define SI(i) scanf("%d",&i)
#define PI(i) printf("%d\n",i)
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int MAX=2e5+5;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
int dir[9][2]={0,1,0,-1,1,0,-1,0, -1,-1,-1,1,1,-1,1,1};
template<class T>bool gmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>bool gmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>void gmod(T &a,T b){a=(a%mod+b)%mod;}

int bit[20];
ll dp[14][110][110]; //位,sum,%

ll dfs(int pos,int sum,int remain,bool limit,int total)
{
    if(pos<0)return sum==total&&remain==0; //总和等于枚举的那个和total
    if(!limit&&dp[pos][sum][remain]!=-1)return dp[pos][sum][remain];
    int up=limit?bit[pos]:9;
    ll res=0;
    for(int i=0;i<=up;i++)
    {
        if(sum+i>total)break;
        res+=dfs(pos-1,sum+i,(remain*10+i)%total,limit&&i==up,total);
    }
    if(!limit) return dp[pos][sum][remain]=res;
    return res;
}
ll solve(ll x)
{
    int pos=0;
    for(;x;x/=10)bit[pos++]=x%10;
    ll ans=0;
    for(int i=1;i<=9*pos;i++)
    {
        memset(dp,-1,sizeof(dp));
        ans+=dfs(pos-1,0,0,1,i);
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    rep(cas,1,T)
    {
        ll n,ans=0;
        scanf("%lld",&n);
        printf("Case %d: %lld\n",cas,solve(n));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值