數位dp(dfs版)

題目:http://acm.split.hdu.edu.cn/showproblem.php?pid=3555

題意:給定一個上限n,求1--n中存在多少個數,數中含有49

題解:dfs數位dp,此題一共三種狀態:st:0:前一個數爲4的狀態,此位加9就可滿足題意;1:前面數字中已經含有49;2:前面中沒有49並且前一位不是4

狀態轉移:

狀態 4 9 其他數字
0 0 1 2
1 1 1 1
2 0 2 2

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
long long dp[25][5];
int num[25];//分解輸入上限
long long dfs(int pos,int st,bool f)//表示dp[pos][st]爲pos位st狀態,f代表pos位是否受限,即表示可以取0-9任意數字還是規定了最大值
{
    if(pos<=0) return (!f&&st==1)?1:0;//只有在st爲滿足題意得狀態時才返回1,此題爲1狀態時  後面原理同此
    if(!f && dp[pos][st]!=-1) return dp[pos][st];
    int limit=f?num[pos]:9;//若受限就用分解的最大值來規定最大值
    long long ans=0;
    for(int i=0;i<=limit;i++)
    {
        bool next_f =f & (limit==i);//是否受限以及是否達到受限最大值
        if(i==4)
        {
            if(st==0) ans+=dfs(pos-1,0,next_f);
            if(st==1) ans+=dfs(pos-1,1,next_f);
            if(st==2) ans+=dfs(pos-1,0,next_f);
        }
        else if(i==9)
        {
            if(st==0) ans+=dfs(pos-1,1,next_f);
            if(st==1) ans+=dfs(pos-1,1,next_f);
            if(st==2) ans+=dfs(pos-1,2,next_f);
        }
        else
        {
            if(st==0) ans+=dfs(pos-1,2,next_f);
            if(st==1) ans+=dfs(pos-1,1,next_f);
            if(st==2) ans+=dfs(pos-1,2,next_f);
        }
    }
    if(!f) dp[pos][st]=ans;//若不受限就是統計了後面位數的數字和,可以記錄
    return ans;
}
long long solve(long long d)
{
    int pos=1;
    while(d)
    {
        num[pos++]=d%10;d/=10;
    }
    return dfs(pos-1,2,1);//注意要從2狀態開始
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int t;
    scanf("%d",&t);
    while(t--)
    {
        long long right;
        scanf("%lld",&right);
        printf("%lld\n",solve(right+1));
    }
    return 0;
}

題目:http://acm.split.hdu.edu.cn/showproblem.php?pid=2089

題意:給定上限和下限,求在這範圍內不含62和4的數字個數

題解:dfs數位dp,狀態:st:0:滿足含有62或4的數字的個數;1:不含62和4中前一位數字是6,+2就變成滿足題意;2:不含62和4並且前一位不是6,狀態同上一題

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
long long dp[25][5];
int num[25];
long long dfs(int pos,int st,bool f)
{
    if(pos<=0) return (!f&&st==0)?1:0;//同意從滿足題意得狀態返回1
    if(!f && dp[pos][st]!=-1) return dp[pos][st];
    int limit=f?num[pos]:9;
    long long ans=0;
    for(int i=0;i<=limit;i++)
    {
        bool next_f =f & (limit==i);
        if(i==4)
        {
            if(st==0) ans+=dfs(pos-1,0,next_f);
            if(st==1) ans+=dfs(pos-1,0,next_f);
            if(st==2) ans+=dfs(pos-1,0,next_f);
        }
        else if(i==6)
        {
            if(st==0) ans+=dfs(pos-1,0,next_f);
            if(st==1) ans+=dfs(pos-1,1,next_f);
            if(st==2) ans+=dfs(pos-1,1,next_f);
        }
        else if(i==2)
        {
            if(st==0) ans+=dfs(pos-1,0,next_f);
            if(st==1) ans+=dfs(pos-1,0,next_f);
            if(st==2) ans+=dfs(pos-1,2,next_f);
        }
        else
        {
            if(st==0) ans+=dfs(pos-1,0,next_f);
            if(st==1) ans+=dfs(pos-1,2,next_f);
            if(st==2) ans+=dfs(pos-1,2,next_f);
        }
    }
    if(!f) dp[pos][st]=ans;
    return ans;
}
long long solve(long long d)
{
    int pos=1;
    while(d)
    {
        num[pos++]=d%10;d/=10;
    }
    return dfs(pos-1,2,1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    long long left,right;
    while(~scanf("%lld %lld",&left,&right)&&left+right!=0)
    {
        printf("%lld\n",right-left+1-solve(right+1)+solve(left));
    }
    return 0;
}


題目:http://acm.split.hdu.edu.cn/showproblem.php?pid=5898

題意:給定上下限,求在範圍區間內滿足連續奇數位爲偶數並且連續偶數位爲奇數的數字

題解:dfs數位dp,st:0:奇奇;1:奇偶;2:偶奇;3:偶偶;4:前導0(前一位爲0)

狀態轉移:

狀態\加值 0 1、3、5、7、9(奇數) 2、4、6、8(偶數)
0 x 2 x
1 3 0 3
2 1 0 1
3 1 x 1
4 4 0 1

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
long long dp[25][6];
/*
* 奇奇  0
* 奇偶  1
* 偶奇  2
* 偶偶  3
* 前導0 4
*/
int num[25];
long long dfs(int pos,int st,bool f)
{
    if(pos<=0) return (!f&&(st==1||st==2))?1:0;//1、2滿足題意,所以只有他們返回0,原理同上
    if(!f && dp[pos][st]!=-1) return dp[pos][st];
    int limit=f?num[pos]:9;
    long long ans=0;
    for(int i=0;i<=limit;i++)
    {
        bool next_f =f & (limit==i);
        if(i==0)
        {
            if(st==0) ans+=0;
            if(st==1) ans+=dfs(pos-1,3,next_f);
            if(st==2) ans+=dfs(pos-1,1,next_f);
            if(st==3) ans+=dfs(pos-1,1,next_f);
            if(st==4) ans+=dfs(pos-1,4,next_f);
        }
        else if(i & 1)
        {
            if(st==0) ans+=dfs(pos-1,2,next_f);
            if(st==1) ans+=dfs(pos-1,0,next_f);
            if(st==2) ans+=dfs(pos-1,0,next_f);
            if(st==3) ans+=0;
            if(st==4) ans+=dfs(pos-1,0,next_f);
        }
        else
        {
            if(st==0) ans+=0;
            if(st==1) ans+=dfs(pos-1,3,next_f);
            if(st==2) ans+=dfs(pos-1,1,next_f);
            if(st==3) ans+=dfs(pos-1,1,next_f);
            if(st==4) ans+=dfs(pos-1,1,next_f);
        }
    }
    if(!f) dp[pos][st]=ans;
    return ans;
}
long long solve(long long d)
{
    int pos=1;
    while(d)
    {
        num[pos++]=d%10;d/=10;
    }
    return dfs(pos-1,4,1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int t;
    scanf("%d",&t);
    long long left,right;
    for(int i=1;i<=t;i++)
    {

        scanf("%lld %lld",&left,&right);
        printf("Case #%d: %lld\n",i,solve(right+1)-solve(left));
    }
    return 0;
}

可見,模板如下:

long long dp[25][6];
int num[25];
long long dfs(int pos,int st,bool f) //pos 當前位數,st當前狀態,f是否受限
{
    if(pos<=0) return (!f&&(st==xxxx))?1:0;//滿足題意的狀態返回1,其他返回0
    if(!f && dp[pos][st]!=-1) return dp[pos][st];//記憶化,之前計算過的不用再計算
    int limit=f?num[pos]:9;
    long long ans=0;
    for(int i=0;i<=limit;i++)
    {
        bool next_f =f & (limit==i);//判斷下一位是否受限
        if(i==.....)  //枚舉狀態轉移條件,列出狀態轉移方程
        {
            ..........
        }
    }
    if(!f) dp[pos][st]=ans;//記憶化
    return ans;
}
long long solve(long long d) //位數分解
{
    int pos=1;
    while(d)
    {
        num[pos++]=d%10;d/=10;
    }
    return dfs(pos-1,4,1);
}


發佈了59 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章