数位dp总结
Posted Arlenmbx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位dp总结相关的知识,希望对你有一定的参考价值。
/*数位dp模板 int dfs(int i, int s, bool e) { if (i==-1) return s==target_s; if (!e && ~f[i][s]) return f[i][s]; int res = 0; int u = e?num[i]:9; for (int d = first?1:0; d <= u; ++d) res += dfs(i-1, new_s(s, d), e&&d==u); return e?res:f[i][s]=res; } ~~f为记忆化数组; ~~i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数); ~~s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t); ~~e表示之前的数是否是上界的前缀(即后面的数能否任意填)。 ~~for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的, 但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位, 然后外面统计时候枚举一下位数。It depends. */ hdu2089(求区间不包含62和4的数的个数) #include <iostream> #include <string> #include <string.h> #include <algorithm> using namespace std; int dp[10][10]; void init() { memset(dp,0,sizeof(dp)); dp[0][0] = 1; for(int i=1;i<=7;i++) { for(int j=0;j<10;j++) { for(int k=0;k<10;k++) { if(j!=4&&!(j==6&&k==2)) dp[i][j] += dp[i-1][k]; } } } } int solve(int n)//找的范围是<n { init(); int digit[10]; int len = 0; while(n>0) { digit[++len] = n%10; n/=10; } digit[len+1]=0; int ans = 0; for(int i=len;i;i--) { for(int j=0;j<digit[i];j++) { if(j!=4&&!(digit[i+1]==6&&j==2)) ans+=dp[i][j]; } if(digit[i]==4||(digit[i]==2&&digit[i+1]==6)) break; } return ans; } int main() { int l,r; while(cin>>l>>r) { if(l+r==0) break; else cout<<solve(r+1)-solve(l)<<endl; //由于要找[l,r],而solve函数找的范围为<n,所以传参的时候应该特别注意 } return 0; } (模板解决) #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; int dp[10][2][11]; int bit[15]; int dfs(int pos,int have,int end,bool limit){ if(pos==0) return !have; if(!limit&&dp[pos][have][end]!=-1) return dp[pos][have][end]; long long ans=0; int num=limit?bit[pos]:9; for(int i=0;i<=num;i++){ if((end==6&&i==2)||i==4) ans+=dfs(pos-1,1,i,limit&&i==num); else ans+=dfs(pos-1,have,i,limit&&i==num); } if(!limit) dp[pos][have][end]=ans; return ans; } int cal(long long n){ memset(dp,-1,sizeof(dp)); memset(bit,0,sizeof(bit)); int len=0; while(n){ bit[++len]=n%10; n=n/10; } return dfs(len,0,0,true); } int main(){ int l,r; while(~ scanf("%d%d",&l,&r)){ if(l==0&&r==0) break; // memset(dp,-1,sizeof(dp)); int ans=cal(r)-cal(l-1); printf("%d\n",ans); } return 0; } HDU3652(求[0,n]区间含子串13并且是13的倍数) #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; int dp[15][15][5]; //一维为位数,二维为余数,三维为目前的状态 int bit[15]; ///have==0,,,,末尾不是1&&无13 ///hava==1,,,,末尾是1 &&无13 ///hava==2,,,,含有13 int dfs(int pos,int mod,int have,int limit){ if(pos==0) return mod==0&&have==2; if(!limit&&dp[pos][mod][have]!=-1) return dp[pos][mod][have]; int ans=0; int num=limit?bit[pos]:9; for(int i=0;i<=num;i++){ int mod_t=(mod*10+i)%13; int have_x=have; if(have==0&&i==1) have_x=1; if(have==1&&i!=1) have_x=0; if(have==1&&i==3) have_x=2; ans+=dfs(pos-1,mod_t,have_x,limit&&i==num); } if(!limit) dp[pos][mod][have]=ans; return ans; } int main(){ int n; while(scanf("%d",&n)!=EOF){ int len=0; memset(dp,-1,sizeof(dp)); memset(bit,0,sizeof(bit)); while(n){ bit[++len]=n%10; n=n/10; } int cnt=dfs(len,0,0,1); printf("%d\n",cnt); } return 0; } /* * HDU 3652 B-number(简单化) * 含有数字13和能够被13整除的数的个数 * dp[i][j][k][z]:i:处理的数位,j:该数对13取模以后的值,k:是否已经包含13,z结尾的数 */ #include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> using namespace std; int dp[12][15][2][10]; int bit[12]; int dfs(int pos,int num,bool t,int e,bool flag) { if(pos==-1)return t&&(num==0); if(!flag && dp[pos][num][t][e]!=-1) return dp[pos][num][t][e]; int end=flag?bit[pos]:9; int ans=0; for(int i=0;i<=end;i++) ans+=dfs(pos-1,(num*10+i)%13,t||(e==1&&i==3),i,flag&&(i==end)); if(!flag)dp[pos][num][t][e]=ans; return ans; } int calc(int n) { int pos=0; while(n) { bit[pos++]=n%10; n/=10; } return dfs(pos-1,0,0,0,1); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; memset(dp,-1,sizeof(dp)); while(scanf("%d",&n)==1) { printf("%d\n",calc(n)); } return 0; } codeforce55d /* * 题意:求区间[x , y]中beautiful number的个数, * a positive integer number is beautiful if and only * if it is divisible by each of its nonzero digits. 分析:一个数能被它的所有非零数位整除,则能被它们的最小公倍数整除,而1到9的最小公倍数为2520, 数位DP时我们只需保存前面那些位的最小公倍数就可进行状态转移,到边界时就把所有位的lcm求出了, 为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,显然要记录值是不可能的,其实我们只 需记录它对2520的模即可,这样我们就可以设计出如下数位DP:dfs(pos,mod,lcm,f),pos为当前 位,mod为前面那些位对2520的模,lcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限, 这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能 是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。 */ #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; const int maxn=25; const int mod=2520; int bit[maxn]; long long dp[maxn][mod][48]; int index[mod+10]; void init(){ int cnt=0; for(int i=1;i<=mod;i++){ if(mod%i==0) index[i]=cnt++; } } int gcd(int a,int b){ if(b==0) return a; else return gcd(b,a%b); } int lcm(int a,int b){ return a/gcd(a,b)*b; } long long dfs(int pos,int presum,int prelcm,bool limit){ if(pos==0) return presum%prelcm==0; if(!limit&&dp[pos][presum][index[prelcm]]!=-1) return dp[pos][presum][index[prelcm]]; long long ans=0; int num=limit?bit[pos]:9; for(int i=0;i<=num;i++){ int nowsum=(presum*10+i)%mod; int nowlcm=prelcm; if(i){ nowlcm=lcm(nowlcm,i); } ans+=dfs(pos-1,nowsum,nowlcm,limit&&i==num); } if(!limit) dp[pos][presum][index[prelcm]]=ans; return ans; } long long calc(long long n){ memset(bit,0,sizeof(bit)); int len=0; while(n){ bit[++len]=n%10; n=n/10; } return dfs(len,0,1,true); } int main(){ int t; init(); memset(dp,-1,sizeof(dp)); scanf("%d",&t); while(t--){ long long l,r; scanf("%I64d%I64d",&l,&r); printf("%I64d\n",calc(r)-calc(l-1)); } return 0; } POJ 3252Round Numbers 给定一个区间L,R 求这个区间里面每个数的二进制形式中0比1的数的总个数 #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; const int maxn=35; int dp[maxn][maxn][maxn]; int bit[maxn]; int dfs(int pos,int num0,int num1,bool first,bool limit ){ if(pos==0) return num0>=num1; if(!limit&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1]; int ans=0; int num=limit?bit[pos]:1; for(int i=0;i<=num;i++){ if(first){ if(i==0) ans+=dfs(pos-1,0,0,first,limit&&i==num); else ans+= dfs(pos-1,num0,num1+1,false,limit&&i==num); } else{ if(i==0) ans+=dfs(pos-1,num0+1,num1,false,limit&&i==num); else ans+=dfs(pos-1,num0,num1+1,false,limit&&i==num); } } if(!limit) dp[pos][num0][num1]=ans; return ans; } int cal(int n){ int len=0; memset(dp,-1,sizeof(dp)); memset(bit,0,sizeof(bit)); while(n){ bit[++len]=n&1; n>>=1; } return dfs(len,0,0,1,1); } int main(){ int l,r; while(scanf("%d%d",&l,&r)!=EOF){ memset(dp,-1,sizeof(dp)); int ans=cal(r)-cal(l-1); printf("%d\n",ans); } return 0; } HDU 3709 Balanced Number 求一个区间内的平衡数 对此题平衡数的定义为, 4139 is a balanced number with pivot fixed at 3. The torqueses are 4*2 + 1*1 = 9 and 9*1 = 9, for left part and right part, #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; int bit[20]; long long dp[20][20][2000]; long long dfs(int pos,int point,int sum,bool limit){ if(pos==0) return sum==0; if(sum<0) return 0; if(!limit&&dp[pos][point][sum]!=-1) return dp[pos][point][sum]; long long ans=0; int num=limit?bit[pos]:9; for(int i=0;i<=num;i++){ ans+=dfs(pos-1,point,i*(pos-point)+sum,limit&&i==num); } if(!limit) dp[pos][point][sum]=ans; return ans; } long long cal(long long n){ memset(dp,-1,sizeof(dp)); memset(bit,0,sizeof(bit)); int len=0; while(n){ bit[++len]=n%10; n=n/10; } long long sum=0; for(int i=1;i<=len;i++) sum+=dfs(len,i,0,true); return sum-len+1; } int main(){ int t; scanf("%d",&t); while(t--){ long long l,r; scanf("%lld%lld",&l,&r); long long ans=cal(r)-cal(l-1); printf("%lld\n",ans); } return 0; } hdu4507 F(x) For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A). Sample Input 3 0 100 1 10 5 100 Sample Output Case #1: 1 Case #2: 2 Case #3: 13 输入A,B 求在区间【0,b】内f(x)的值不大于a的值得数量 dp[i][j]表示i位数中权值不大于j的数量 #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; int dp[20][200000]; int bit[20]; //int a,b; int dfs(int pos,int sum,bool limit){ if(pos==0) return sum>=0; if(sum<0) return 0; if(!limit&&dp[pos][sum]!=-1) return dp[pos][sum]; int res=0; int end=limit?bit[pos]:9; for(int i=0;i<=end;i++){ res+=dfs(pos-1,sum-i*(1<<(pos-1)),limit&&i==end); } if(!limit) dp[pos][sum]=res; return res; } int f(int a){ int len=0; int res=0; while(a){ res+=(a%10)*(1<<len); len++; a=a/10; } return res; } int a,b; int cal(){ int len=0; while(b){ bit[++len]=b%10; b=b/10; } return dfs(len,f(a),true); } int main(){ int t; scanf("%d",&t); int cas=0; memset(dp,-1,sizeof(dp)); while(t--){ cas++; // memset(bit,0,sizeof(bit)); scanf("%d%d",&a,&b); int ans=cal(); printf("Case #%d: %d\n",cas,ans); } return 0; }
/*数位dp模板
int dfs(int i, int s, bool e) {
if (i==-1) return s==target_s;
if (!e && ~f[i][s]) return f[i][s];
int res = 0;
int u = e?num[i]:9;
for (int d = first?1:0; d <= u; ++d)
res += dfs(i-1, new_s(s, d), e&&d==u);
return e?res:f[i][s]=res;
}
~~f为记忆化数组;
~~i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);
~~s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);
~~e表示之前的数是否是上界的前缀(即后面的数能否任意填)。
~~for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,
但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,
然后外面统计时候枚举一下位数。It depends.
*/
hdu2089(求区间不包含62和4的数的个数)
#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
int dp[10][10];
void init()
{
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=7;i++)
{
for(int j=0;j<10;j++)
{
for(int k=0;k<10;k++)
{
if(j!=4&&!(j==6&&k==2))
dp[i][j] += dp[i-1][k];
}
}
}
}
int solve(int n)//找的范围是<n
{
init();
int digit[10];
int len = 0;
while(n>0)
{
digit[++len] = n%10;
n/=10;
}
digit[len+1]=0;
int ans = 0;
for(int i=len;i;i--)
{
for(int j=0;j<digit[i];j++)
{
if(j!=4&&!(digit[i+1]==6&&j==2))
ans+=dp[i][j];
}
if(digit[i]==4||(digit[i]==2&&digit[i+1]==6))
break;
}
return ans;
}
int main()
{
int l,r;
while(cin>>l>>r)
{
if(l+r==0)
break;
else
cout<<solve(r+1)-solve(l)<<endl; //由于要找[l,r],而solve函数找的范围为<n,所以传参的时候应该特别注意
}
return 0;
}
(模板解决)
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[10][2][11];
int bit[15];
int dfs(int pos,int have,int end,bool limit){
if(pos==0)
return !have;
if(!limit&&dp[pos][have][end]!=-1)
return dp[pos][have][end];
long long ans=0;
int num=limit?bit[pos]:9;
for(int i=0;i<=num;i++){
if((end==6&&i==2)||i==4)
ans+=dfs(pos-1,1,i,limit&&i==num);
else
ans+=dfs(pos-1,have,i,limit&&i==num);
}
if(!limit)
dp[pos][have][end]=ans;
return ans;
}
int cal(long long n){
memset(dp,-1,sizeof(dp));
memset(bit,0,sizeof(bit));
int len=0;
while(n){
bit[++len]=n%10;
n=n/10;
}
return dfs(len,0,0,true);
}
int main(){
int l,r;
while(~ scanf("%d%d",&l,&r)){
if(l==0&&r==0)
break;
// memset(dp,-1,sizeof(dp));
int ans=cal(r)-cal(l-1);
printf("%d\n",ans);
}
return 0;
}
HDU3652(求[0,n]区间含子串13并且是13的倍数)
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[15][15][5];
//一维为位数,二维为余数,三维为目前的状态
int bit[15];
///have==0,,,,末尾不是1&&无13
///hava==1,,,,末尾是1 &&无13
///hava==2,,,,含有13
int dfs(int pos,int mod,int have,int limit){
if(pos==0)
return mod==0&&have==2;
if(!limit&&dp[pos][mod][have]!=-1)
return dp[pos][mod][have];
int ans=0;
int num=limit?bit[pos]:9;
for(int i=0;i<=num;i++){
int mod_t=(mod*10+i)%13;
int have_x=have;
if(have==0&&i==1)
have_x=1;
if(have==1&&i!=1)
have_x=0;
if(have==1&&i==3)
have_x=2;
ans+=dfs(pos-1,mod_t,have_x,limit&&i==num);
}
if(!limit)
dp[pos][mod][have]=ans;
return ans;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
int len=0;
memset(dp,-1*sizeof(dp));
memset(bit,0,sizeof(bit));
while(n){
bit[++len]=n%10;
n=n/10;
}
int cnt=dfs(len,0,0,1);
printf("%d\n",cnt);
}
return 0;
}
/*
* HDU 3652 B-number(简单化)
* 含有数字13和能够被13整除的数的个数
* dp[i][j][k][z]:i:处理的数位,j:该数对13取模以后的值,k:是否已经包含13,z结尾的数
*/
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
using namespace std;
int dp[12][15][2][10];
int bit[12];
int dfs(int pos,int num,bool t,int e,bool flag)
{
if(pos==-1)return t&&(num==0);
if(!flag && dp[pos][num][t][e]!=-1)
return dp[pos][num][t][e];
int end=flag?bit[pos]:9;
int ans=0;
for(int i=0;i<=end;i++)
ans+=dfs(pos-1,(num*10+i)%13,t||(e==1&&i==3),i,flag&&(i==end));
if(!flag)dp[pos][num][t][e]=ans;
return ans;
}
int calc(int n)
{
int pos=0;
while(n)
{
bit[pos++]=n%10;
n/=10;
}
return dfs(pos-1,0,0,0,1);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n;
memset(dp,-1,sizeof(dp));
while(scanf("%d",&n)==1)
{
printf("%d\n",calc(n));
}
return 0;
}
codeforce55d
/*
* 题意:求区间[x , y]中beautiful number的个数,
* a positive integer number is beautiful if and only
* if it is divisible by each of its nonzero digits.
分析:一个数能被它的所有非零数位整除,则能被它们的最小公倍数整除,而1到9的最小公倍数为2520,
数位DP时我们只需保存前面那些位的最小公倍数就可进行状态转移,到边界时就把所有位的lcm求出了,
为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,显然要记录值是不可能的,其实我们只
需记录它对2520的模即可,这样我们就可以设计出如下数位DP:dfs(pos,mod,lcm,f),pos为当前
位,mod为前面那些位对2520的模,lcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限,
这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能
是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。
*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=25;
const int mod=2520;
int bit[maxn];
long long dp[maxn][mod][48];
int index[mod+10];
void init(){
int cnt=0;
for(int i=1;i<=mod;i++){
if(mod%i==0)
index[i]=cnt++;
}
}
int gcd(int a,int b){
if(b==0)
return a;
else
return gcd(b,a%b);
}
int lcm(int a,int b){
return a/gcd(a,b)*b;
}
long long dfs(int pos,int presum,int prelcm,bool limit){
if(pos==0)
return presum%prelcm==0;
if(!limit&&dp[pos][presum][index[prelcm]]!=-1)
return dp[pos][presum][index[prelcm]];
long long ans=0;
int num=limit?bit[pos]:9;
for(int i=0;i<=num;i++){
int nowsum=(presum*10+i)%mod;
int nowlcm=prelcm;
if(i){
nowlcm=lcm(nowlcm,i);
}
ans+=dfs(pos-1,nowsum,nowlcm,limit&&i==num);
}
if(!limit)
dp[pos][presum][index[prelcm]]=ans;
return ans;
}
long long calc(long long n){
memset(bit,0,sizeof(bit));
int len=0;
while(n){
bit[++len]=n%10;
n=n/10;
}
return dfs(len,0,1,true);
}
int main(){
int t;
init();
memset(dp,-1,sizeof(dp));
scanf("%d",&t);
while(t--){
long long l,r;
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",calc(r)-calc(l-1));
}
return 0;
}
POJ 3252Round Numbers
给定一个区间L,R
求这个区间里面每个数的二进制形式中0比1的数的总个数
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=35;
int dp[maxn][maxn][maxn];
int bit[maxn];
int dfs(int pos,int num0,int num1,bool first,bool limit ){
if(pos==0)
return num0>=num1;
if(!limit&&dp[pos][num0][num1]!=-1)
return dp[pos][num0][num1];
int ans=0;
int num=limit?bit[pos]:1;
for(int i=0;i<=num;i++){
if(first){
if(i==0)
ans+=dfs(pos-1,0,0,first,limit&&i==num);
else
ans+= dfs(pos-1,num0,num1+1,false,limit&&i==num);
}
else{
if(i==0)
ans+=dfs(pos-1,num0+1,num1,false,limit&&i==num);
else
ans+=dfs(pos-1,num0,num1+1,false,limit&&i==num);
}
}
if(!limit)
dp[pos][num0][num1]=ans;
return ans;
}
int cal(int n){
int len=0;
memset(dp,-1,sizeof(dp));
memset(bit,0,sizeof(bit));
while(n){
bit[++len]=n&1;
n>>=1;
}
return dfs(len,0,0,1,1);
}
int main(){
int l,r;
while(scanf("%d%d",&l,&r)!=EOF){
memset(dp,-1,sizeof(dp));
int ans=cal(r)-cal(l-1);
printf("%d\n",ans);
}
return 0;
}
HDU 3709 Balanced Number
求一个区间内的平衡数
对此题平衡数的定义为, 4139 is a balanced number with pivot fixed at 3. The torqueses are 4*2 + 1*1 = 9 and 9*1 = 9, for left part and right part,
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int bit[20];
long long dp[20][20][2000];
long long dfs(int pos,int point,int sum,bool limit){
if(pos==0)
return sum==0;
if(sum<0)
return 0;
if(!limit&&dp[pos][point][sum]!=-1)
return dp[pos][point][sum];
long long ans=0;
int num=limit?bit[pos]:9;
for(int i=0;i<=num;i++){
ans+=dfs(pos-1,point,i*(pos-point)+sum,limit&&i==num);
}
if(!limit)
dp[pos][point][sum]=ans;
return ans;
}
long long cal(long long n){
memset(dp,-1,sizeof(dp));
memset(bit,0,sizeof(bit));
int len=0;
while(n){
bit[++len]=n%10;
n=n/10;
}
long long sum=0;
for(int i=1;i<=len;i++)
sum+=dfs(len,i,0,true);
return sum-len+1;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
long long l,r;
scanf("%lld%lld",&l,&r);
long long ans=cal(r)-cal(l-1);
printf("%lld\n",ans);
}
return 0;
}
hdu4507 F(x)
For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A).
Sample Input
3
0 100
1 10
5 100
Sample Output
Case #1: 1
Case #2: 2
Case #3: 13
输入A,B
求在区间【0,b】内f(x)的值不大于a的值得数量
dp[i][j]表示i位数中权值不大于j的数量
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[20][200000];
int bit[20];
//int a,b;
int dfs(int pos,int sum,bool limit){
if(pos==0)
return sum>=0;
if(sum<0)
return 0;
if(!limit&&%[email protected][pos][sum]!=-1)
return dp[pos][sum];
int res=0;
int end=limit?bit[pos]:9;
for(int i=0;i<=end;i++){
res+=dfs(pos-1,sum-i*(1<<(pos-1)),limit&&i==end);
}
if(!limit)
dp[pos][sum]=res;
return res;
}
int f(int a){
int len=0;
int res=0;
while(a){
res+=(a%10)*(1<<len);
len++;
a=a/10;
}
return res;
}
int a,b;
int cal(){
int len=0;
while(b){
bit[++len]=b%10;
b=b/10;
}
return dfs(len,f(a),true);
}
int main(){
int t;
scanf("%d",&t);
int cas=0;
memset(dp,-1,sizeof(dp));
while(t--){
cas++;
// memset(bit,0,sizeof(bit));
scanf("%d%d",&a,&b);
int ans=cal();
printf("Case #%d: %d\n",cas,ans);
}
return 0;
}
以上是关于数位dp总结的主要内容,如果未能解决你的问题,请参考以下文章