zoukankan      html  css  js  c++  java
  • hdu3652(含有13且能被13整除的数)数位DP基础

    B-number

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 3257    Accepted Submission(s): 1819


    Problem Description
    A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task is to calculate how many wqb-numbers from 1 to n for a given integer n.
     
    Input
    Process till EOF. In each line, there is one positive integer n(1 <= n <= 1000000000).
     
    Output
    Print each answer in a single line.
     
    Sample Input
    13 100 200 1000
     
    Sample Output
    1 1 2 2
     
    Author
    wqb0039
     
    Source
     
    Recommend
    lcy
    这道题跟只是将不要62的那道题稍微变形了一下,dp[i][j][k]长度为i,对13取余为j的数。
    dp[i][j][0]代表不含13的整数的个数
    dp[i][j][1]代表不含13,且首位为3的整数的个数
    dp[i][j][2]代表含有13的整数的个数
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    const int N=12;
    int md[N],dp[N][13][3];
    
    void Init(){
        md[0]=1;
        for(int i=1;i<N;i++)
            md[i]=md[i-1]*10;
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        for(int i=0;i<N-1;i++)
            for(int j=0;j<13;j++){
                for(int k=0;k<10;k++)
                    dp[i+1][(j+md[i]*k)%13][0]+=dp[i][j][0];
                dp[i+1][(j+md[i])%13][0]-=dp[i][j][1];
                dp[i+1][(j+md[i]*3)%13][1]+=dp[i][j][0];
                dp[i+1][(j+md[i])%13][2]+=dp[i][j][1];
                for(int k=0;k<10;k++)
                    dp[i+1][(j+md[i]*k)%13][2]+=dp[i][j][2];
            }
    }
    int solve(int x)
    {
        int s[15],len=0,xx=x;
        while(x>0)
        {
            s[++len]=x%10;
            x/=10;
        }
        s[len+1]=0; //初始化前缀为0,0是没有任何影响的,后面一位可能会用到前面一位
           // cout<<len<<endl;
        int ans=0,flag=0;
        int yy=0,yyy=0;
        int answer;
         for(int i=len;i>=1;i--) //每次枚举的数是:上界前缀+i这一位的数字+符合要求的dp[i][j][k]
        {
            answer=ans;
            for(int kk=0;kk<s[i];kk++)
            {
    
                for(int j=0;j<13;j++)
                {
                    if(( (yyy*10+ kk)*md[i-1] +j)%13==0)    //i这一位上枚举的数字变化,就得判断
                    {
                       ans+=dp[i-1][j][2];
                    }
                 }
    
            }
            if(flag)  //如果前缀中有出现13,并且
            {
               // ans+=s[i]*dp[i-1][0];
                for(int kk=0;kk<s[i];kk++)
                for(int j=0;j<13;j++)
                {
                     if(( (yyy*10+ kk)*md[i-1] +j)%13==0)
                    {
                        ans+=dp[i-1][j][0];
                    }
                }
            }
            //只考虑以len位置为i的开头的数
            if(!flag && s[i]>1)
            {
               // ans+=dp[i-1][1];//因为是大于号,所以低一位可以完全枚举,加上首位为3的个数
                 for(int j=0;j<13;j++)
                {
                     if(( (yyy*10+ 1)*md[i-1] +j)%13==0)
                    {
                        ans+=dp[i-1][j][1];
                    }
                }
            }
            //考虑前缀的影响
            if(!flag && s[i+1]==1 && s[i]>3)
              //  ans+=dp[i][1];
              {
                  for(int j=0;j<13;j++)
                  {
                     if(( ((yyy-s[i+1]+1)*10+ 3)*md[i-1] + j )%13==0)
                     {
                         ans+=dp[i-1][j][0];
                     }
                  }
              }
            if(s[i+1]==1 && s[i]==3)
            {
                flag=1;
            }
            yyy=yyy*10+s[i];
        }
        return ans;
    }
    int main(){
    
      //  freopen("test.txt","r",stdin);
      //cout<<(0%13)<<endl;
        Init();
        int n;
        while(~scanf("%d",&n)){
            printf("%d
    ",solve(n+1));
        }
        return 0;
    }

    小优化的代码:

    已知(a+b)%13=0,已知a,求b;

    1:(a+b)%13=0;

    2:(a%13+b%13)%13=0;

    3:(13-a%13)%13=b;(第二次再MOD13,是因为a有可能等于0)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    const int V=10;
    
    const int mod=13;
    const int N=12;
    int md[N],dp[N][13][3];
    
    void Init(){
        md[0]=1;
        for(int i=1;i<N;i++)
            md[i]=md[i-1]*10%13;
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        for(int i=0;i<N-1;i++)
            for(int j=0;j<13;j++){
                for(int k=0;k<10;k++)
                    dp[i+1][(j+md[i]*k)%13][0]+=dp[i][j][0];
                dp[i+1][(j+md[i])%13][0]-=dp[i][j][1];
                dp[i+1][(j+md[i]*3)%13][1]+=dp[i][j][0];
                dp[i+1][(j+md[i])%13][2]+=dp[i][j][1];
                for(int k=0;k<10;k++)
                    dp[i+1][(j+md[i]*k)%13][2]+=dp[i][j][2];
            }
    }
    
    int solve(int x)
    {
        int s[15],len=0,xx=x;
        while(x>0)
        {
            s[++len]=x%10;
            x/=10;
        }
        s[len+1]=0; //初始化前缀为0,0是没有任何影响的,后面一位可能会用到前面一位
           // cout<<len<<endl;
        int ans=0,flag=0;
        int yy=0,yyy=0;
        int answer;
         for(int i=len;i>=1;i--)
        {
         //   ans+=(s[i]*dp[i-1][2]);  //含4和62的个数
            answer=ans;
            int temp_mod;
            for(int kk=0;kk<s[i];kk++)
            {
                 temp_mod = (13 - ( (yyy * 10 + kk) * md[i-1] )% 13) %13;
                 ans+=dp[i-1][temp_mod][2] ;
                 if(flag)  //如果前缀中有出现13,并且
                    ans+=dp[i-1][temp_mod][0];
                 if(!flag && kk==1)
                    {
                        ans+=dp[i-1][temp_mod][1];
                    }
                 if(!flag && s[i+1]==1 && kk==3)
                     ans+=dp[i-1][temp_mod][0];
    
            }
            //只考虑以len位置为i的开头的数
            if(s[i+1]==1 && s[i]==3)
            {
                flag=1;
    
            }
            yyy=yyy*10+s[i];
           // printf("%d
    ",ans-answer);
        }
        return ans;
    }
    
    int main()
    {
    
      //  freopen("test.txt","r",stdin);
      //cout<<(0%13)<<endl;
        Init();
        int n;
        while(~scanf("%d",&n)){
            printf("%d
    ",solve(n+1));
        }
        return 0;
    }
  • 相关阅读:
    还在使用golang 的map 做Json编码么?
    Golang 性能测试(2) 性能分析
    golang 性能测试 (1) 基准性能测试
    消息队列 NSQ 源码学习笔记 (五)
    消息队列 NSQ 源码学习笔记 (四)
    消息队列 NSQ 源码学习笔记 (三)
    消息队列 NSQ 源码学习笔记 (二)
    消息队列 NSQ 源码学习笔记 (一)
    你不知道的空格
    Supervisor 使用和进阶4 (Event 的使用)
  • 原文地址:https://www.cnblogs.com/xianbin7/p/4731823.html
Copyright © 2011-2022 走看看