首页 > 代码库 > hdu3652(数位dp)

hdu3652(数位dp)

题意:求1-n(n<=1000000000)的数中,技能被13整除,又包含13子串的数的个数;


解法:数位dp。LL dp[pre][now][have][iflow][rem]记录着第pre位为now,have表示前边是否出现过13,iflow表示dp过程否有降低,rem表示后续需要的余数是多少。


代码:

/******************************************************
* author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;

#define eps 1e-8
const double pi=acos(-1.0);
typedef long long LL;
const int Max=10100;
const int INF=1000000007;
LL dp[20][20][2][2][15];
int reminder[20];
int num[30];
LL dfs(int pre,int now,bool have,bool iflow,int rem)
{
    if(pre==0)
        return have&&rem==0;
    if(dp[pre][now][have][iflow][rem]!=-1)
        return dp[pre][now][have][iflow][rem];
    int en=iflow? 9 : num[pre-1];
    LL ans=0;
    for(int i=0;i<=en;i++)
    {
        int tool=((rem-(i*reminder[pre-1])%13)+13)%13;
        if(now==1&&i==3)
        ans+=dfs(pre-1,i,1,iflow||i!=en,tool);
        else
        ans+=dfs(pre-1,i,have,iflow||i!=en,tool);
    }
    return dp[pre][now][have][iflow][rem]=ans;
}
void init()
{
    reminder[0]=1;
    for(int i=1;i<=10;i++)
        reminder[i]=(reminder[i-1]*10)%13;
}
LL solve(LL n)
{
    int p=0;
    memset(dp,-1,sizeof dp);
    while(n)
    {
        num[p++]=n%10;
        n/=10;
    }
    LL ans=0;
    for(int i=0;i<=num[p-1];i++)
    {
        int tool=(i*reminder[p-1])%13;
        ans+=dfs(p-1,i,0,i!=num[p-1],(13-tool)%13);
    }
    return ans;
}
int main()
{
  int n;
  init();
  while(cin>>n)
  {
      cout<<solve(n)<<‘\n‘;
  }
   return 0;
}