P2188 小Z的 k 緊湊數 題解(數位DP)
阿新 • • 發佈:2018-11-12
題目連結
解題思路
數位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; }