【HDU3652】B-number-数位DP

测试地址:B-number

题目大意:给定一些整数,求以这些整数为n时,区间[0,n]中有多少既含有“13”这个子串又能被13整除的数(称为B数)。

做法:以前没写过数位DP的记忆化搜索写法,代码风格是向这位大神学习的。记忆化搜索时是从高位到低位搜索,而dp数组的计算是从低位到高位计算的,和迭代写法差不多。dfs函数有五个参数:pos,pre,mod,limit,flag,分别表示当前在第pos位(从低位到高位数),第pos+1位是pre,在这之前构成的数字(为了简便称为s)被13除余数为mod,limit表示当前位有没有被限制,flag表示s中含不含有“13”子串。设dp[i][j][k]为剩下i位数未确定,s被13除余数为j,s特性为k时可能有的B数个数,其中k=0表示s含“13”子串,k=1表示s不含“13”子串但最低位为1,k=2表示s不含“13”子串且最低位不为1。如果走到的状态无法直接用已算出的状态表示,枚举第pos位,注意限制,继续往下搜索。然后注意搜索回来的时候,如果当前位是被限制的,那么不能向dp数组中写入数据,否则根据当前状态的特性向dp数组中写入数据。详情参看代码。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,ans,bit[20];
int dp[20][20][3];

int dfs(int pos,int pre,int mod,bool limit,bool flag)
{
  if (pos<=0) return flag&&(mod==0);
  if (!limit&&flag&&dp[pos][mod][0]!=-1) return dp[pos][mod][0];
  if (!limit&&!flag&&pre!=1&&dp[pos][mod][2]!=-1) return dp[pos][mod][2];
  if (!limit&&!flag&&pre==1&&dp[pos][mod][1]!=-1) return dp[pos][mod][1];
  
  int up=limit?bit[pos]:9,ret=0;
  for(int i=0;i<=up;i++)
    ret+=dfs(pos-1,i,(mod*10+i)%13,limit&&(i==up),flag||(pre==1&&i==3));
  
  if (!limit)
  {
    if (flag) dp[pos][mod][0]=ret;
	if (pre!=1&&!flag) dp[pos][mod][2]=ret;
	if (pre==1&&!flag) dp[pos][mod][1]=ret;
  }
  
  return ret;
}

int solve(int n)
{
  int len=0;
  while(n)
  {
    bit[++len]=n%10;
	n/=10;
  }
  return dfs(len,0,0,1,0);
}

int main()
{
  memset(dp,-1,sizeof(dp));
  while(scanf("%d",&n)!=EOF)
  {
	printf("%d
",solve(n));
  }
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793767.html