數位dp

數位dp的思想在於:對於例如725這個數,我們計算1-725或者0-725之間的所有情況。

從最高爲開始:有0,1,2,3,4,5,6,7.

其中:兩個數字最爲關鍵:0,7,而其他數字則是包含了以後所有的情況。

故我們需要對這兩個數字採用兩個bool類型來判定。

一共就有四種情況。

性質:

1.數位dp中,經常關注每個數位上的值,這就導致在很多情況下,由於數位的增長非常容易的原因,導致滿足符合條件的情況很多,因此對於滿足條件的情況增長也是非常快的。

HDU - 6644 這個題的關鍵點就是發現數位的增長非常快,我們可以簡化dp的複雜度。

dp[i][j]記錄第i位模爲j的個數

 

數位dp模板:

struct Num
{
    int dig[100];
    int len;
    int rad;
    Num(){len=0;}
    void read(int x,int radix)
    {
        rad=radix;
        len=0;
        while(x)
        {
            len++;
            dig[len]=x%rad;
            x=x/rad;
        }
    }
    void trans(int radix)
    {
        int num=0;
        for(int i=len;i>=1;i--)
            num=num*rad+dig[i];
        read(num,radix);
    }
};

struct Digdp
{
    ll a[40][20];  // 數位,數值,其他
    Num num;

    Digdp(){memset(a,-1,sizeof(a));}
    ll read(Num x)
    {
        num=x;

        ll ans=0;
        int maxNum=num.dig[num.len];
        for(int i=0;i<maxNum;i++)
            ans=ans+dfs(num.len,i,0);
        ans=ans+dfs(num.len,maxNum,1);
        return ans;
    }
    ll dfs(int pos,int val,bool limit)
    {

        if(limit==0)
        {
            if(a[pos][val]!=-1)
                return a[pos][val];
            /*handle*/
        }
        /*handle*/
    }
};

具體細節部分如下:

struct Num
{
    int dig[100],len,rad;
    Num(){len=0;}
    void read(int x,int radix)
    {
        rad=radix;len=0;
        while(x)
        {
            len++;
            dig[len]=x%rad;
            x=x/rad;
        }
    }
    void trans(int radix)
    {
        int num=0;
        for(int i=len;i>=1;i--)num=num*rad+dig[i];
        read(num,radix);
    }
};

struct Digdp
{
    ll a[40][20];  // 數位,數值,其他
    Num num;
    Digdp(){memset(a,-1,sizeof(a));}
    ll read(Num x)
    {
        num=x;
        ll ans=0;
        int maxNum=num.dig[num.len];
        for(int i=0;i<maxNum;i++)ans=ans+dfs(num.len,i,0,1);
        ans=ans+dfs(num.len,maxNum,1,0);
        return ans;
    }
    ll dfs(int pos,int val,bool limit,bool isz)
    {
        if(pos==0)return 0;
        if(limit==0)//用來判定當前是否被限制了!對於全0的情況,肯定包含在沒有被限制之中
        {           //由於沒有被限制,需要存儲dp結果。
            if(a[pos][val]!=-1)return a[pos][val];
            a[pos][val]=0;               //對於全0,和非全0 運算步驟上沒有區別,只是條件分支不一樣
            for(int i=1;i<num.rad;i++)a[pos][val]=a[pos][val]+dfs(pos-1,i,0,0);   //其他數字
            if(isz==1)a[pos][val]=a[pos][val]+dfs(pos-1,0,0,1); //當前全0,後面是0 ,依然沒限制,全0
            else a[pos][val]=a[pos][val]+dfs(pos-1,0,0,0); //當前非全0,後面是0 ,依然沒限制,非0
            return a[pos][val];
        }
        else
        {    //三部分組成  0 1-num-1 num
            ll ans=0;
            int dig=num.dig[pos-1];
            ans=ans+dfs(pos-1,dig,1,0);    //計算被限制部分
            if(dig!=0)                     //被限制部分非0,計算其他部分
            {
                ans=ans+dfs(pos-1,0,0,0);
                for(int i=1;i<dig;i++)ans=ans+dfs(pos-1,i,0,0);
            }
            return ans;
        }
    }
};

 

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