题目:
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 21
using 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 11
using 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;
}