描述:數字dp的問題題型一般是給定乙個閉區間[l,r],求這個區間中滿足「某種條件」的數的個數的總數
對於這類問題,我們首先統計[l,r]範圍的滿足條件的數字轉化成統計[1,n]內滿足條件的數字的數量
那麼ans[l,r]=ans[1,r]-ans[1,l-1];
這樣接下來討論問題我們只需要考慮上邊界即可
現在考慮統計[1,12345]內「魔法數的數量」
首先我們將長度<=5的數字補全前導0到五位
1->00001 ,123->00123
這樣的對映一定是一一對應的,假設數字x補全前導0後為x
那麼乙個數x<=12345等同於x字典序<=12345
我們把這兩種限制條件合併到了考慮數字的維度
最直觀的解決方法:從最高位到最低位列舉,我們用dfs來解決這個過程
對於這道題的dfs要記錄的狀態
1.現在列舉到了哪一位
2.前面的一位數字是多少
3.這一位可以填哪些數字
由此可見對於x有兩種可能性
(1):如果x前面的某一位已經小於上限數字對應位子的數字,這一位可以填0-9
(2):如果x前面的每一位數字都等於上線數字對應的數字,這一位可填的數字範圍為0-上限數字對應位置的數字
所以我們只需要維護前面每一位是否都和上上限數字一樣,就可以得到這個位置可填範圍
int
dsf(
int pos,
int pre_num,
bool flag)
}return ret;
}
現在dfs即為暴力列舉,複雜度為o(upper_bound)
接下來考慮如何優化這個dfs
我們發現dfs狀態(pos,pre_num,flag),如果flag為false,那麼dfs計算的過程和upper_bound毫無關係了,我們可以考慮這個這個狀態的答案記錄下來,這就是數字dp的思想所在
int
dsf(
int pos,
int pre_num,
bool flag)}if
(!flag)dp[pos]
[pre_num]
=ret;
//更新+存起來
return ret;
}
通過前面的一道題,我們已經得到解決數字dp的一套方法模板
對於不同的問題,我們只需要修改dfs中狀態的定義以及轉移即可
hdu-2089不要62
#include
#include
#include
#include
#define ll long long
using
namespace std;
ll m,n;
int name[55]
;int dp[55]
[20],b[55]
;int
dfs(
int x,
int pre_num,
bool flag)if(
!flag&&dp[x]
[pre_num]!=-
1)return dp[x]
[pre_num]
;int maxs;
if(flag)maxs=name[x]
;else maxs=9;
int res=0;
for(
int i=
0;i<=maxs;i++)if
(!flag)dp[x]
[pre_num]
=res;
return res;
}int
prime
(int x)
return
dfs(r-1,
0,true);
}int
main()
}
數字dp例題
在了解數字dp之前,先來看乙個問題 求a b中不包含49的數的個數.0 a b 2 10 9 注意到n的資料範圍非常大,暴力求解是不可能的,考慮dp,如果直接記錄下數字,陣列會開不起,該怎麼辦呢?要用到數字dp.數字dp一般應用於 求出在給定區間 a,b 內,符合條件p i 的數i的個數.條件p i...
數字dp 計數問題 模板題 數字dp
數字dp目前見的比較少,最為經典的莫過於不要62,或許以前都是暴力求解。例如求解1 n中,數字x出現的次數這類題,暴力列舉每個數的時間複雜度為 o n o n o n 再列舉每一位的時間複雜度為 log 10nlog n log10n 數字一大妥妥超時。值得一提的是,2020 的藍橋杯,第一道簽到題...
區間dp(模板 例題)
參考博文 區間dp小結 附經典例題 首先,什麼是區間dp?它是幹什麼的?先在小區間進行dp得到最優解,然後再利用小區間的最優解合併求大區間的最優解 操作往往涉及到區間合併問題 以上。模板如下 mst dp,0 初始化dp陣列 for int i 1 i n i for int len 2 len n...