1. 程式人生 > >大都會 J Beautiful Numbers (數位dp之數位和問題)

大都會 J Beautiful Numbers (數位dp之數位和問題)

題目描述

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;
}