數位dp介紹


不了解dp的可以先看一下dp

數位dp含義:

數位:一個數有個位,十位,百位,千位等等,數的每一位都是數位。

數位dp歸為計數dp,是在數位上進行操作的dp。

數位dp的實質是一種快速枚舉的方式,它滿足dp的性質,然后進行記憶化搜索。

用途:

有兩個數,兩個數范圍很大(例如1e9,甚至更大),求這兩個數符合限定條件的個數。純暴力不行,就要用數位dp。

例子:求從0到n,(n為2^32-1),(條件)求包含49的數有多少; 

思路or具體實現:

n為2^32-1,數位其實只有20位,枚舉數位,就不會超時。

dp[shuwei][diaojian]。dp的第一維通常是數位,后面的幾維根據題目條件來設定。上面給的例子只用了一維。

控制上界枚舉,從最高位往下枚舉。用記憶化搜索來做,拋開循環后轉移狀態能更加隨意,大部分數位和動態規化的題都可隨意切換。搜索與循環異曲同工之妙,但前者更易轉移狀態,在限制較多的情況下被大部分人喜愛。

例題:Bomb

思路 :數位dp=dfs+記憶化搜索。

需要注意上限即題目所給范圍的預處理,本題用digtis[20] 數組,存儲上限的數位,最好用一個函數來處理,比如solve(sum),處理時,對上限的數位總數拿一個變量進行存儲,比如k或len。

在dfs中用 limit (有些題解是top)判定上限,dfs(len,條件,limit);

dfs執行數位dp,在搜索時用了 up_bound=(limit?digit[len]:9); 來標記上限,同時用cnt來存儲滿足的條件的數量,然后更新dp數組,更新時要滿足if(!limit),到達上界,狀態不完整。

在dfs中對條件的處理,需要根據題意去確定,每個題目不一樣。

是否頂着上界,每層確定這一位選啥,判斷是否和上一位沖突,全部確定完了方案數+1。

由於頂着上界是比較特殊的情況,所以這類答案直接一層層搜索出答案,不用記憶化,其他(不頂着上界)的情況用dp[][] 直接返回數量。

有些題目需要最后算一下最高位為0的情況。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

int digit[20]; //儲存上界的每個數位 
LL dp[20][2];  //統計沒有49的總數 

//if4它的上一位和當前位是否是4
//len 記錄當前數位,從高位往下搜索 
//limit 上一位是否是上界 

LL dfs(int len, bool if4, bool limit){
    if(len==0) return 1ll;  //個位的時候,有一個分支。 
    if(!limit && dp[len][if4])  return dp[len][if4];
    //沒到達上界並且數位已經統計過,直接返回數量。 
    
    LL cnt=0,up_bound=(limit?digit[len]:9);
    //標記數位的上界 
    
    //對整個數位進行記憶化搜索 
    for(int i=0;i<=up_bound;++i){
        if(if4&&i==9) continue;//碰到49不加入。
        cnt+=dfs( len-1, i==4,limit && i==up_bound);
        //向下搜索,判斷上一位是否為4,上一位是否到達上界,當前位是否到上界。 
    } 
    if(!limit) dp[len][if4]=cnt; //到達上界是狀態不完整,不更新dp 
    return cnt;//直接返回本次搜索結果,加入到最后結果中 
}

LL solve(LL num) //num是上界這個數 
{
    int k=0;//記錄數位個數。
    while(num)
    {
        digit[++k]=num%10;
        num/=10; 
    } 
    return dfs(k,false,true); 
} 

int main(){
     int t;
    cin>>t;
    while(t--)
    {
        LL n;
        cin>>n;
        cout<<n+1-solve(n)<<endl;
    } 
    return 0;
}
數位dp詳解

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM