HDU 3652 B-number

数位DP

dp[i][j][k][m]表示最高位为i,数字j在首位,之前是否出现过13,余数是m的情况下的个数

代码有详细注释,做完这题,感觉逐渐了解了数位DP

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <string>
using namespace std;

int dp[15][15][5][15];
int tot,n;
int p[20];

bool cheak(int a,int b,int c,int d)
{
    int res=a;
    for(int i=1;i<=b-1;i++) res=((res*10)%13);
    if((res+c)%13==d) return 1;
    return 0;
}

bool check2(int a,int b)
{
    int num=0;
    for(int i=tot;i>=1;i--)
    {
        if(i>=a+1) num=num*10+p[i];
        else num=num*10;
    }
    if((num%13+b)%13==0) return 1;
    return 0;
}

void init()
{
    memset(dp,0,sizeof dp);

    for(int j=0;j<=9;j++) dp[1][j][0][j]=1;

    for(int i=2;i<=10;i++)
    {
        for(int j=0;j<=9;j++)
        {
            for(int k=0;k<=1;k++)
            {
                for(int l=0;l<=12;l++)
                {
                    int sum=0;
                    if(k==0)//推到第i位的时候都没有13
                    {
                        for(int s=0;s<=9;s++)//枚举i-1位是多少
                        {
                            if(j==1&&s==3) continue;
                            for(int m=0;m<=12;m++)//枚举余数
                                if(cheak(j,i,m,l))
                                    sum=sum+dp[i-1][s][0][m];
                        }
                        dp[i][j][k][l]=sum;
                    }
                    else if(k==1)//推到第i位的时候有13,可能之前就有13,也可能i位和i-1位产生了13
                    {
                        //之前就有13
                        for(int s=0;s<=9;s++)
                            for(int m=0;m<=12;m++)//枚举余数
                                if(cheak(j,i,m,l))
                                    sum=sum+dp[i-1][s][1][m];

                        //i位和i-1位产生了13
                        for(int s=0;s<=9;s++)
                            if(j==1&&s==3)//这一位是1,上一位是3
                                for(int m=0;m<=12;m++)//枚举余数
                                    if(cheak(j,i,m,l))
                                        sum=sum+dp[i-1][s][0][m];//从之前没有13的情况推导过来
                        dp[i][j][k][l]=sum;
                    }
                }
            }
        }
    }
}

int f(int x)
{
    tot=1;
    while(x)
    {
        p[tot++]=(x%10);
        x=x/10;
    }
    tot--;

    int res=0;

    //计算位数比tot小的总和
    for(int i=1;i<tot;i++)
    {
        for(int j=1;j<=9;j++)
        {
            res=res+dp[i][j][1][0];
        }
    }

    //计算位数为tot,但首位比x首位小的数量总和
    for(int i=1;i<p[tot];i++) res=res+dp[tot][i][1][0];

    //计算剩余部分
    bool flag=0;
    for(int i=tot-1;i>=1;i--)
    {
        for(int j=0;j<p[i];j++)
        {
            if(flag==0)
            {
                if(j==3&&p[i+1]==1)
                {
                    for(int m=0;m<=12;m++)
                    {
                        if(check2(i,m))
                        {
                            res=res+dp[i][j][0][m];
                            res=res+dp[i][j][1][m];
                        }
                    }
                }
                else
                {
                    for(int m=0;m<=12;m++)
                    {
                        if(check2(i,m))
                        {
                            res=res+dp[i][j][1][m];
                        }
                    }
                }
            }
            else if(flag==1)
            {
                for(int m=0;m<=12;m++)
                {
                    if(check2(i,m))
                    {
                        res=res+dp[i][j][0][m];
                        res=res+dp[i][j][1][m];
                    }
                }
            }
        }
        if(p[i]==3&&p[i+1]==1) flag=1;//表示之前已经出现过13了
    }

    return res;
}

int main()
{
    init();
    while(~scanf("%d",&n))
        printf("%d\n",f(n+1));
    return 0;
}

 

posted @ 2016-01-28 18:35  Fighting_Heart  阅读(214)  评论(0编辑  收藏  举报