您的位置:首页 > 其它

HDU 3652 B-number

2016-01-28 18:35 232 查看
数位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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: