1. 程式人生 > >P2188 小Z的 k 緊湊數 題解(數位DP)

P2188 小Z的 k 緊湊數 題解(數位DP)

題目連結

小Z的 k 緊湊數

解題思路

數位DP,把每一個數位的每一個數對應的可能性表示出來,然後求\(num(1,r)-num(1,l-1)\),其中\(num(i,j)\)表示\([i,j]\)區間裡符合要求的數的個數。
其中,\(dp[i][j]\)表示第\(i\)位數字為\(j\)的選擇種數。
計算的時候,比如\(num(456)\),就拆開為\(num(1,99)+num(100,399)+num(400,449)+num(450,455)+num(456,456)\)

AC程式碼

#include<stdio.h>
long long k,dp[20][14],l,r;
int absf(int a){
    if(a<0)return -a;
    return a;
}
void dpf(){
    int i,j,m;
    for(i=0;i<=9;i++)dp[0][i]=1;//個位數,初始化為1
    for(i=1;i<20;i++)//這是總共的位數
        for(j=0;j<=9;j++)//這是這一位
            for(m=0;m<=9;m++)//這是上一位
                if(absf(j-m)<=k)dp[i][j]+=dp[i-1][m];//這一位和上一位滿足條件則加上
}
long long num(long long x){
    int n[20]={0},cnt=0,i,j;
    long long ans=0;
    while(x>0){
        n[cnt++]=x%10;
        x/=10;
    }
    //首位為0 
    for(i=0;i<cnt-1;i++)
        for(j=1;j<=9;j++)
            ans+=dp[i][j];
    //首位為[1,n[cnt-1]) 
    if(cnt>0)for(i=1;i<n[cnt-1];i++)ans+=dp[cnt-1][i];
    //首位為n[cnt-1] 
    for(i=cnt-2;i>=0;i--){
        for(j=0;j<n[i];j++){
            if(absf(n[i+1]-j)<=k)ans+=dp[i][j];
        }
        if(absf(n[i+1]-n[i])>k)break;
        //非常重要!!前幾位已經不滿足絕對值之差不大於k之後就不能再繼續下去了 
        if(!i&&absf(n[i+1]-j)<=k)ans+=dp[i][j];//這裡相當於計算那個num(456,456)
    }
    if(cnt==1)ans++;//這裡也相當於計算那個num(456,456),但是個位數不會進入上面那個迴圈
    return ans;
}
int main(){
    scanf("%lld%lld%lld",&l,&r,&k);
    dpf();
    printf("%lld",num(r)-num(l-1));
    return 0;
}