數字dp例題模板題(後續更新)

2021-10-09 18:12:04 字數 2035 閱讀 9199

描述:數字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...