數位dp(入門)

這一週一直在學dp,先學了一些簡單的dp,然後又學了狀壓dp,這次又學了數位dp,唉,感覺這dp是真的難,可能是我太菜了吧,沒辦法,慢慢學吧,本來準備一週把dp學完是不太可能了,因爲今天已經週六了,還是進入正題,說我們的數位dp吧。

數位dp是一種計數用的dp,一般就是要統計一個區間[le,ri]內滿足一些條件數的個數。所謂數位dp,字面意思就是在數位上進行dp咯。數位還算是比較好聽的名字,數位的含義:一個數有個位、十位、百位、千位......數的每一位就是數位啦!之所以要引入數位的概念完全就是爲了dp。數位dp的實質就是換一種暴力枚舉的方式,使得新的枚舉方式滿足dp的性質,然後記憶化就可以了。

這是抄的別人的解釋,下面說一下我自己對數位dp的額理解吧,狀態dp你運算的時候,是以每個數字所代表的二進制的每一位來爲運算對象的,而數位dp呢,它是以數字的每一個數字爲運算對象的,一個數字的個位,十位,百位……

解題思路:用數位dp的題一般都是讓你求一個區間中,滿足一些特徵的數字的個數,例入求一個區間的奇數的個數……這樣的題一般的數據的範圍都比較大,就是讓你用正常的暴力是根本寫不出來的,就算寫出來了那麼也一定會超時,這樣我們就需要一中特殊的方法進行暴力,那就是數位dp了,數位dp也是一種暴力,只不過他相當於一種記憶化的暴力,就是說它的暴力比較省時間,就像提前打好了表一樣,這樣一般都不壺超時了,

首先我們每次都可以先構造一個關於dp的二維數組dp[i][j]。代表的意思是一個數字的長度爲i,以j數字開頭的時候,所有符合特徵的數字。遞推關係 dp[i][j]=sum(dp[i-1][k]),這個數組求好後,如果我們再去求一個區間的一些特殊的數字的話,我們就可以縮小這個區間的範圍了,

下面我們看一道題,來領略一節數位dp的魅力吧。

P2657 [SCOI2009]windy數

這個題的大致意思就是

windy定義了一種windy數。不含前導零且相鄰兩個數字之差至少爲2的正整數被稱爲windy數。 windy想知道,

在A和B之間,包括A和B,總共有多少個windy數?

這個題就很符合我們所說的數位dp的特徵了啊,下面給出AC代碼。

#include<bits/stdc++.h>
using namespace std;
const int maxl=12;
int a[maxl];
int dp[maxl][maxl];
//dp[i][j]代表的意思是一個數字的長度爲i,以j數字開頭的時候,所有的windy數字
//遞推關係 dp[i][j]=sum(dp[i-1][k]) abs(j-k)>=2;
void init()            //先把dp數組的值給求出來;
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<10;i++)
        dp[1][i]=1;
    for(int i=2;i<maxl;i++)
    {
        for(int j=0;j<=9;j++)
        {
            for(int k=0;k<=9;k++)
            {
                if(abs(k-j)>=2) dp[i][j]+=dp[i-1][k];
            }
        }
    }
}
int A(int n)
{
    memset(a,0,sizeof(a));
    int cnt=0,sum=0;
    while(n)          //分解n的每一個數字,並求出n的長度
    {
        cnt++;
        a[cnt]=n%10;
        n=n/10;
    }
    for(int i=1;i<cnt;i++)    //比這個數字長度小的數字都可以
        for(int j=1;j<=9;j++)
            sum+=dp[i][j];
    for(int i=1;i<a[cnt];i++)//長度相同,但是首位比n小的數字
        sum+=dp[cnt][i];
    for(int i=cnt-1;i>=1;i--) //剩餘的部分,這一部分就有難了。
    {
        for(int j=0;j<=a[i]-1;j++)
        {
            if(abs(a[i+1]-j)>=2) sum+=dp[i][j];
        }
        if(abs(a[i]-a[i+1])<2) break;
    }
    return sum;
}
int main()
{
    int l,r;
    init();
    cin>>l>>r;
    cout<<A(r+1)-A(l)<<endl;
    return 0;
}
















 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章