HDU-3652 B-number (数位dp)

来源:互联网 发布:喜马拉雅fm无网络连接 编辑:程序博客网 时间:2024/06/05 17:44

题目:

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

题意:

计算区间内是13的倍数并且包含13的数的个数。

思路:

数位dp,既要包含13又要是13的倍数,所以有2个状态。具体见代码注释。

代码:

预处理写法:

//kopyh#include <bits/stdc++.h>#define INF 0x3f3f3f3f#define MOD 1000000007#define EPS 1e-6#define N 21using namespace std;int n,m,sum,res,flag;//dp[i][j][k]第i位余数为j的是否包含13的数的个数,k==0不包含,k==1首位为3,k==2包含13//a[i] 1ei的余数int dp[N][13][3],a[N];void init(){//计算每个位数的余数    a[0]=1;    for(int i=1;i<N;i++)        a[i] = a[i-1]*10%13;    memset(dp,0,sizeof(dp));    dp[0][0][0] = 1;    for(int i=1;i<N;i++)//迭代位数        for(int j=0;j<13;j++)//枚举余数        {            for(int k=0;k<10;k++)//枚举首位数字            {dp[i][(j+a[i-1]*k)%13][0]+=dp[i-1][j][0];dp[i][(j+a[i-1]*k)%13][2]+=dp[i-1][j][2];}//记录首位为3的个数dp[i][(j+a[i-1]*3)%13][1] += dp[i-1][j][0];//把新包含13的个数调整到dp[i][][2]中            dp[i][(j+a[i-1])%13][0] -= dp[i-1][j][1];            dp[i][(j+a[i-1])%13][2] += dp[i-1][j][1];        }}int solve(int x){//取出数位    int dig[N],len=0;    while(x)dig[len++]=x%10,x/=10;    dig[len]=0;    int flag=0,ans=0,mod=0;//从高位到低位枚举每一位数    for(int i=len-1;i>=0;i--)    {//枚举当前位为j,得到前面的余数为(mod+j*a[i])%13,//后面位数中余数是(13-(mod+j*a[i])%13)%13的个数都是答案。        for(int j=0;j<dig[i];j++)            ans+=dp[i][(13-(mod+j*a[i])%13)%13][2];        if(flag)        {//前面已经有了13,只要是前面和后面所有位数的余数为0的就是答案。            for(int j=0;j<dig[i];j++)                ans+=dp[i][(13-(mod+j*a[i])%13)%13][0];        }        else        {//首位是3的余数和为0就是答案。            if(dig[i+1]==1&&dig[i]>3)                ans+=dp[i+1][(13-mod)%13][1];//取当前位为1后面首位为3的是答案。            if(dig[i]>1)                ans+=dp[i][(13-(mod+a[i])%13)%13][1];        }//已经出现过13,标记。        if(dig[i+1]==1&&dig[i]==3)flag=1;        mod=(mod+dig[i]*a[i])%13;    }    return ans;}int main(){    int i,j,k,kk,cas,T,t,x,y,z;    init();    while(scanf("%d",&n)!=EOF)        printf("%d\n",solve(n+1));    return 0;}


深搜写法:

//kopyh#include <bits/stdc++.h>#define INF 0x3f3f3f3f#define MOD 1000000007#define EPS 1e-6#define N 11using namespace std;int n,m,sum,res,flag;int dp[N][13][3],dig[N];//len:当前位,mod:前面留下的余数大小,k:前面是否出现13,flag:前面每一位是否都是上限。int dfs(int len, int mod, int k, int flag){    if(len<=0)return (!mod && k==2);    //通过dp优化    if(!flag && dp[len][mod][k]!=-1)return dp[len][mod][k];    //通过是否为上限确定当前位可取的最大值    int num = flag?dig[len]:9;    int ans=0;    for(int i=0;i<=num;i++)    {        int modt = (mod*10+i)%13;        //确定是否已出现13        int kt;        if(k==2 || k==1&&i==3)kt=2;        else if(i==1)kt=1;        else kt=0;                ans+=dfs(len-1,modt,kt,flag&&num==i);    }    if(!flag)dp[len][mod][k] = ans;    return ans;}int main(){    int i,j,k,kk,cas,T,t,x,y,z;    while(scanf("%d",&n)!=EOF)    {        memset(dp,-1,sizeof(dp));        sum=0;        while(n)dig[++sum]=n%10,n/=10;        printf("%d\n",dfs(sum,0,0,1));    }    return 0;}













0 0
原创粉丝点击