hdu 2089 不要62 数位dp

不要62

  HDU - 2089 
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
LL dp[10][10];
int a[10];
void init()
{
    dp[0][0]=1;//dp[i][j]代表i位数,最高位为j含有多少满足情况的数字 例如 f[2][6]={60 61 63 65 66 67 68 69}
    for(int i=1;i<10;i++)  //枚举位数
    {
        for(int j=0;j<10;j++)//枚举最高位
        {
            if(j==4)dp[i][j]=0;//如果最高位为4 那没直接为0
            else if(j==6)     //如果最高位是6为话下一位就不能为2
            {
                for(int k=0;k<10;k++)
                {
                    dp[i][j]+=dp[i-1][k];
                }
                dp[i][j]-=dp[i-1][2];
            }
            else
            {
                for(int k=0;k<10;k++)
                    dp[i][j]+=dp[i-1][k];
            }
        }
    }
}
LL solve(LL n)
{
    memset(a,0,sizeof(a));
    int pos=1;   //把一个数分解例如3678 分解为 8 7 6 3
    while(n)
    {
        a[pos++]=n%10;
        n/=10;
    }
    LL ans=0;  
    for(int i=pos-1;i>=1;i--)//从最高位开始即从3开始  每次使枚举的最高位严格小于a[i].,这样就能得到所有的值
    {
        for(int j=0;j<a[i];j++)//
        {
            if(!(a[i+1]==6&&j==2))//符合条件的
            ans+=dp[i][j];
        }
        if(a[i+1]==6&&a[i]==2)break;//如果当前不满足条件的话就不用继续加了,因为我们已经把小于当前数满足条件的都找出来了。
        if(a[i]==4)break;
    }
    return ans;
}
int main()
{
    LL n,m;
    init();
    ios::sync_with_stdio(0);
    while(cin>>n>>m)
    {
        if(n==0&&m==0)break;
        cout<<solve(m+1)-solve(n)<<endl;
    }
}
/*开始我也有疑惑为啥每次枚举严格小于当前a[i]的就能找到所有的答案并且当前不符合条件就退出
比如我要计算0-3678有多少的符合条件的数字
我从最高位开始枚举0 1 2
0的时候我计算了0-999
1的时计算了1000-1999
2 ----2000-2999
然后枚举第三位6
0  (3)000-(3)099 其实3000- 3099 之间满足的就是0-99
1  (3)100-(3)199
2   3200-3299
3   3300-3399
4   400-499
5   500-599
枚举第2位7
0  (360)0-3609  
1  (36)10-(36)19 //这里是假设当前最高位之前的数字都已经相等了 3610-3619 之间满足的就等于 10 -19
2   36 20 - 36 29
3   ......
6   3660 - 3669
枚举第一位
3670-3677
这样我们发现 我们每次传入一个n 计算的是(0-n-1)之间满足条件的数字有多少个
所以当我们要求n-m之间满足情况的有多少个 我们直接 solve(m+1)-solve(n)就可以了。*/
参考了大牛的博客,发现了递归写法,看起来更简单一些。
https://blog.csdn.net/wust_zzwh/article/details/52100392
附上递归写法:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
int a[15];
LL f[20][2];
LL dfs(int pos ,int pre,int sta,int limit)
{
    if(pos==-1)return 1;
    if(!limit&&f[pos][sta]!=-1)return f[pos][sta];
    LL cnt=0;
    int up=limit? a[pos]:9;
    for(int i=0;i<=up;i++)
    {
        if(pre==6 && i==2)continue;
        if(i==4)continue;
        cnt+=dfs(pos-1,i,i==6,limit&&i==a[pos]);
        //cout<<"pos="<<pos<<" i="<<i<<" "<<" cnt="<<cnt<<endl;
    }
    if(!limit) f[pos][sta]=cnt;
    return cnt;
}
LL slove(LL n)
{
    int pos=0;
    while(n)
    {
        a[pos++]=n%10;
        n/=10;
    }
    return dfs(pos-1,-1,0,1);
}
int main()
{
    LL n,m;
    memset(f,-1,sizeof f);
    while(cin>>m>>n&&m+n)
    {
        cout<<slove(n)-slove(m-1)<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/80811844
今日推荐