# Acwing 1082 數字遊戲
阿新 • • 發佈:2020-07-14
Acwing 1082 數字遊戲 [數位DP詳解+模板]
數位 DP 問題往往都是這樣的題型,給定一個閉區間\([L,R]\),讓你求這個區間中滿足某種條件的數的總數。
字首和思想,轉化為\(f([0,R])-f([0,L-1])\)求解。
轉化成求\(f(N)\),將上限N轉化成10進位制(根據題意轉化為K進位制,一般是十進位制),列舉從最高位開始列舉N的10進位制的每一位,只要該位的取值小於N的10進位制對應位的取值,那麼這個數必然小於等於N。
一般使用DFS求解。
inline int dfs(int pos,int pre,bool limit,bool lead)
一般要記錄當前在處理哪一位,當前位前一位的相關資訊,當前位是否有上限限制,當前位是否有前導0限制(最高位不能取0)
//limit向下一位傳遞的條件
limit==1&&i==a[pos] //當前位有限制,並且當前位取到了限制下的最大值
//lead向下一位傳遞的條件
lead==1&&i==0 //當前位有前導0限制(當前位不能取0),並且當前位取的就是0
例題 Acwing 1082 數字遊戲
題意
求[X,Y]中的數,滿足數字從左到右非下降關係的數的個數,如:123,445.
思路
數位dp經典題。
字首和思想,轉化為\(f([0,R])-f([0,L-1])\)求解。
轉化成求\(f(N)\),將上限N轉化成10進位制(根據題意轉化為K進位制,一般是十進位制),列舉從最高位開始列舉N的10進位制的每一位,只要該位的取值小於N的10進位制對應位的取值,那麼這個數必然小於等於N。
程式碼
#include <bits/stdc++.h> using namespace std; //f[i][j]表示前i位,第i+1位為j時的滿足條件的數的總數 //同時充當標記陣列,DFS使用記憶化搜尋,一般將f[][]初始化為-1,遞迴過程中如果出現f[i][j]!=-1,表示這個狀態已經搜尋過,直接返回答案 //本題只需要記錄一個值(數的個數),如果一個狀態需要記錄多個值,f陣列定義為結構體 int f[12][12]; int a[12];//用於儲存上限N的K進位制下的每個數 inline int dfs(int pos,int pre,bool limit){//pos表示當前處理的位,per記錄前一位的資訊,這裡是記錄前一位的值,limit表示當前位是否有上限的限制 if(!pos)return 1;//遞迴出口 if(!limit&&f[pos][pre]!=-1)return f[pos][pre];//當前位沒有限制,並且已經搜尋過,直接返回答案 int up=limit?a[pos]:9;//獲取當前位的取值上限,如果有限制,上限為n的對應位的值,否則上限為9 int res=0; for(int i=0;i<=up;++i){//列舉當前位的取值 if(i<pre)continue;//由於要滿足不下降數的限制,如果當前位的取值比上一位小,當前位取值不合法,沒有必要處理當前位取i時後面位的取值 //當前位取值合法,遞迴列舉下一位的取值,下一位上限有限制的條件是limit==1&&a==a[pos](也就是說當前位有限制,並且當前位取了上限值。 //比如123,百位取1,百位首先肯定是有限制的,因為百位最大取1,此時該位也取到了上限值1,那麼下一位十位也是有限制的,不能超過2)。而如果當前位取0,那麼十位就能隨便取值了0-9都能取。 res+=dfs(pos-1,i,limit&&i==a[pos]); } //記憶化搜尋,只有在當前狀態沒有任何限制的時候(沒有最高位限制和前導0限制),才表示這個狀態可以通用。 //return (limit||lead)?res:f[pos][pre]=res; return limit?res:f[pos][pre]=res; } inline int sol(int n){ if(!n)return 1;//特殊處理n==0的情況 memset(f,-1,sizeof f);//記憶化陣列兼標記陣列初始化為-1,有些狀態可能本來就是0,所以初始化為-1 //獲取n的10進位制下的每一位 int pos=0; while (n)a[++pos]=n%10,n/=10; return dfs(pos,-1,true); } int main(){ int l,r; while (cin>>l>>r){ cout<<sol(r)-sol(l-1)<<endl;//轉化為字首和求解 } return 0; }