hdu 3555 含有49的数 数位dp

题目:

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

题意:

给定任意n,计算从1~n中有多少数包含49

分析:

今天看到群里有个人问这道题,我就做了一下,dfs就搞定了。。看了一下题解,大部分是递推,找规律求解。但是做题还是dfs

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
ll f[20][3]; //依次表示当前位不是4且不含49,是4,有了49.这三个状态
int s[20];
ll dfs(int pos,int is,bool lim)
{
    if(pos<=0)return is==2;
    if(!lim&&f[pos][is]!=-1)return f[pos][is];
    int num=lim?s[pos]:9;
    ll ans=0;
    for(int i=0;i<=num;i++){
        int iis=0;
        if(is==2)iis=2;
        else if(is==1&&i==9)iis=2;
        else if(i==4)iis=1;

        ans+=dfs(pos-1,iis,lim&&i==num);
    }
    if(!lim)f[pos][is]=ans;
    return ans;
}
int main()
{
    memset(f,-1,sizeof(f));
    int T,n;
    cin>>T;
    while(T--){
        ll n;
        cin>>n;
        int len=0;
        while(n){
            s[++len]=n%10;
            n/=10;
        }
//        for(int i=0;i<=len;i++)cout<<s[i];
//        cout<<endl;
        cout<<dfs(len,0,1)<<endl;
    }
    return 0;
}

递推:

偷点懒,copy点别人写的:http://www.cnblogs.com/sjy123/p/3247731.html

dp[i][0]=dp[i-1][0]*10-dp[i-1][1];                         //代表不含49 (当位数增加一位,不含49的数增加10倍,但是要减去一个以9开头的)
dp[i][1]=dp[i-1][0];                                             //代表不含49,但以9开头(当位数增加一位,就增长了在原来不以9开头的那些)
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];                       //代表包含49(当位数增加一位,之前已经包含49的增加10倍,另外要加上现在开头为4和之前开头为9组合成49的数)

当然状态转移方程不止这一种,如:

 

dp[k][0]=dp[k-1][0]*9+dp[k-1][1]*8;                   //dp[][0]表示不包含49并且以非4结尾的个数

dp[k][1]=dp[k-1][0]+dp[k-1][1];                          //dp[][1]表示不包含49并且以4结尾的个数

dp[k][2]=dp[k-1][1]+dp[k-1][2]*10;                     //dp[][2]表示包含49的个数


#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
ll f[20][3];
int s[20];
void init()
{
    f[0][0]=1;
    f[0][1]=0;
    f[0][2]=0;
    for(int i=1;i<20;i++){
        f[i][0]=f[i-1][0]*10-f[i-1][1];
        f[i][1]=f[i-1][0];
        f[i][2]=f[i-1][1]+f[i-1][2]*10;
    }
}
int main()
{
    init();
    int T,n;
    cin>>T;
    while(T--){
        ll n;
        cin>>n;
        int len=0;
        n++;
        while(n){
            s[++len]=n%10;
            n/=10;
        }
        bool flag=0;
        ll ans=0;
        s[len+1]=0;
        for(int i=len;i>0;i--){
            ans+=f[i-1][2]*s[i];
            if(flag)ans+=f[i-1][0]*s[i];
            if(!flag&&s[i]>4)ans+=f[i-1][1];
            if(s[i+1]==4&&s[i]==9)flag=1;
        }
        cout<<ans<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值