求1~n中不包含49的数的个数
考虑dp
#include
#include
#include
#include
#include
using namespace std;
typedef unsigned long long ULL;
ULL n,t,ans,m;
ULL dp[20][3],buf[20];
void init() {
dp[0][0]=1; dp[0][1]=0; dp[0][2]=0;
for (ULL i=1;i<=22;i++) {
dp[i][0]=dp[i-1][0]*10-dp[i-1][1]; //长度为i不含49的方案数
dp[i][1]=dp[i-1][0]; //长度为i不含49以9开头的方案数
dp[i][2]=dp[i-1][2]*10+dp[i-1][1]; //长度为i且包含49的方案数
}
}
void work() {
memset(buf,0,sizeof(buf)); m=0;
while (n) {
buf[++m]=n%10;
n/=10;
}
buf[m+1]=0;
}
int main() {
init(); cin>>t;
while (t--) {
cin>>n; n++; work();
ULL ans=0;
bool flag=false;
for (ULL i=m;i>=1;i--) {
ULL x=buf[i];
ans+=dp[i-1][2]*x;
if (flag) {
ans+=x*dp[i-1][0]; //相当于i-1位任意取
continue;
}
if (x>4) ans+=dp[i-1][1];
if (buf[i]==9 && buf[i+1]==4) flag=true;
}
cout<<ans<<endl;
}
return 0;
}
求给定的a~b区间内不含62和4的数的个数
刚开始用的第一题一样的dp
#include
#include
#include
#include
#include
using namespace std;
const int N=15;
int n,m;
int dp[N][5],c[N];
void init() {
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=8;i++) {
dp[i][0]=dp[i-1][0]*9-dp[i-1][1]; //长度为i且不包含62和4的个数
dp[i][1]=dp[i-1][0]; //长度为i以2开头且不包含62或4的个数
dp[i][2]=dp[i-1][2]*10+dp[i-1][1]+dp[i-1][0]; //长度为i且包含62或4的个数
}
}
int work(int u) {
int res=0;
memset(c,0,sizeof(c));
while (u!=0) {
c[++res]=u%10;
u/=10;
}
c[res+1]=0;
return res;
}
int solve(int u) {
int res=0;
int len=work(u);
bool flag=false;
for(int i=len;i>=1;i--) {
res+=c[i]*dp[i-1][2];
if(flag) {
res+=c[i]*dp[i-1][0];
continue;
}
if(c[i]>4) res+=dp[i-1][0];
if(c[i]>6) res+=dp[i-1][1];
if(c[i+1]==6 && c[i]>2) res+=dp[i][1];
if(c[i]==4 || (c[i+1]==6 && c[i]==2)) flag=true;
}
return u-res;
}
int main() {
init();
while (~scanf("%d%d",&n,&m) && (n || m)) {
printf("%d\n",solve(m+1)-solve(n));
}
return 0;
}
#include
#include
#include
#include
using namespace std;
const int N=15;
int n,m;
int c[N],dp[N][2]; //pos表示当前位数 pre表示是否有前导的6 limit表示当前位数是否有上限
int dfs(int pos,bool pre,bool limit) {
if (pos==0) return 1;
if (!limit && dp[pos][pre]) return dp[pos][pre]; //记忆化
int res=0,maxx=limit ? c[pos]:9;
for (int i=0;i<=maxx;i++) {
if (i==4 || pre && i==2) continue;
res+=dfs(pos-1,i==6,limit && i==maxx);
}
return limit ? res:dp[pos][pre]=res; //如果有上限就不能记录答案
}
int solve(int u) {
int k=0;
while (u) {
c[++k]=u%10;
u/=10;
}
c[k+1]=0;
return dfs(k,false,true);
}
int main() {
while (~scanf("%d%d",&n,&m)) {
if (!n && !m) continue;
printf("%d\n",solve(m)-solve(n-1));
}
return 0;
}
找出1~n范围内含有13并且能被13整除的数字的个数
#include
#include
#include
#include
using namespace std;
const int N=15;
int n,c[N],dp[N][N][3];
//state==0 什么都没有 state==1 前一位1 state==2 含有13
//mod表示到当前位的模数
int dfs(int pos,int mod,int state,bool limit) {
if (pos==0) return mod==0 && state==2;
if (!limit && dp[pos][mod][state]) return dp[pos][mod][state];
int res=0,maxx=limit ? c[pos]:9;
for (int i=0;i<=maxx;i++) {
int status=0;
if (state==2 || state==1 && i==3) status=2;
else if (i==1) status=1;
res+=dfs(pos-1,(mod*10+i)%13,status,limit && i==maxx);
}
return limit ? res:dp[pos][mod][state]=res;
}
int solve(int u) {
int k=0;
while (u) {
c[++k]=u%10;
u/=10;
}
c[k+1]=0;
return dfs(k,0,0,true);
}
int main() {
while (~scanf("%d",&n)) {
printf("%d\n",solve(n));
}
}
总结一下
数位dp相当于用dfs爆搜每一位的状态
dfs里用到什么就开几维数组去维护它,实现记忆化搜索