我的使用的数位DP与一般的DFS实现的不同,首先数位DP的核心思想看这张树图。从数N的高位到低位处理,首先由于第一位最大是an-1,枚举第一位是(0)~(a)(n-1)(-1)时后面的位数都不受限制,此时我们可以预处理一个数组f[i][j],表示以j开始长度为i(也可能是组合数或者其它)的满足条件数的个数(或某种值),左边枚举的可以直接用f数组求,然后当前位固定为(a)(n-1),再去处理下一位...直到固定的某一个数不符合条件退出,如果能够遍历到最后一位,如果可以再把N算上。
本篇的题是数位DP求区间满足条件的个数。
1081. 度的数量
求给定区间 [X,Y] 中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和。
例如,设 X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:
17=2^4 + 2^0
18=2^4 + 2^1
20=2^4 + 2^2
输入格式
第一行包含两个整数 X 和 Y,接下来两行包含整数 K 和 B。
输出格式
只包含一个整数,表示满足条件的数的个数。
数据范围
1≤X≤Y≤2^31−1,
1≤K≤20,
2≤B≤10
输入样例:
15 20
2
2
输出样例:
3
即B进制下表示有k个1其它为0,我们可以预处理一个数组f[i][j]表示长度为i中选择j位的算法
#include<bits/stdc++.h>
using namespace std;
const int N=40;
int f[N][N],k,b;
int dp(int num){
vector<int> nums;
while(num) nums.push_back(num%b),num/=b;
int res=0,last=0;
for(int i=nums.size()-1;i>=0;--i){
int x=nums[i];
if(x) { //x>=1
res+=f[i][k-last]; //填0
if(x>1){
res+=f[i][k-last-1]; //填1
break; //x大于1 后面的不满足都条件
}
else {
last++; //填1并走到下一位
if(last>k) break;
}
}
if(last==k&&i==0) res++;
}
return res;
}
int main(){
for(int i=0;i<N;++i){
for(int j=0;j<=i;++j){
if(j==0) f[i][j]=1;
else f[i][j]=f[i-1][j]+f[i-1][j-1];
}
}
int l,r;
cin>>l>>r>>k>>b;
cout<<dp(r)-dp(l-1)<<endl;
return 0;
}
1082. 数字游戏
科协里最近很流行数字游戏。
某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 123,446。
现在大家决定玩一个游戏,指定一个整数闭区间 [a,b],问这个区间内有多少个不降数。
输入格式
输入包含多组测试数据。
每组数据占一行,包含两个整数 a 和 b。
输出格式
每行给出一组测试数据的答案,即 [a,b] 之间有多少不降数。
数据范围
1≤a≤b≤231−1
输入样例:
1 9
1 19
输出样例:
9
18
预处理f数组,f[i][j]表示长度为i,第一个数为j的非降序数列个数
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int f[N][N]; //f[i][j] 长度位i,第一个数字是j
int dp(int num){
if(num==0) return 1;
vector<int> nums;
while(num) nums.push_back(num%10),num/=10;
int res=0,last=0;
for(int i=nums.size()-1;i>=0;--i){
int x=nums[i];
for(int j=last;j<x;++j){
res+=f[i+1][j];
}
if(last>x) break;
last=x;
if(i==0) res++;
}
return res;
}
int main(){
for(int i=0;i<10;++i) f[1][i]=1;
for(int i=2;i<N;++i){
for(int j=0;j<10;++j){
for(int k=j;k<10;++k){
f[i][j]+=f[i-1][k];
}
}
}
int l,r;
while(cin>>l>>r) cout<<dp(r)-dp(l-1)<<endl;
}
1085. 不要62
杭州人称那些傻乎乎粘嗒嗒的人为 62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有 4 或 62 的号码。例如:62315,73418,88914 都属于不吉利号码。但是,61152 虽然含有 6 和 2,但不是 连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照号区间 [n,m],推断出交管局今后又要实际上给多少辆新的士车上牌照了。
输入格式
输入包含多组测试数据,每组数据占一行。
每组数据包含一个整数对 n 和 m。
当输入一行为“0 0”时,表示输入结束。
输出格式
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
数据范围
1≤n≤m≤109
输入样例:
1 100
0 0
输出样例:
80
f[i][j]表示长度为i,第一个数字为j的不包含62和4的数列个数
#include<bits/stdc++.h>
using namespace std;
const int N=30;
int f[N][N];
int dp(int num){
if(num==0) return 1;
vector<int> nums;
while(num) nums.push_back(num%10),num/=10;
int res=0,last=0;
for(int i=nums.size()-1;i>=0;--i){
int x=nums[i];
for(int j=0;j<x;++j){
if(last==6&&j==2) continue;
res+=f[i+1][j];
}
if((last==6&&x==2)||x==4) break;
last=x;
if(i==0) res++;
}
return res;
}
int main(){
for(int i=0;i<10;++i) f[1][i]=1;
f[1][4]=0;
for(int i=2;i<N;++i){
for(int j=0;j<10;++j){
if(j==4) continue;
for(int k=0;k<10;++k){
if(k==4) continue;
if(j==6&&k==2) continue;
f[i][j]+=f[i-1][k];
}
}
}
//cout<<f[2][0]<<endl;
int l,r;
while(cin>>l>>r,!(!l&&!r)) cout<<dp(r)-dp(l-1)<<endl;
return 0;
}
1083. Windy数
Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 2 的正整数被称为 Windy 数。
Windy 想知道,在 A 和 B 之间,包括 A 和 B,总共有多少个 Windy 数?
输入格式
共一行,包含两个整数 A 和 B。
输出格式
输出一个整数,表示答案。
数据范围
1≤A≤B≤2×109
输入样例1:
1 10
输出样例1:
9
输入样例2:
25 50
输出样例2:
20
f[i][j]表示长度为i,第一个数字为j的Windy数列个数。注意其他题对于枚举前导0没有限制,例如00014不是windy数,14是windy数,所以在枚举最高位是0时分情况:统计第一位为0的情况+第二位为0的情况...+0的情况
#include<bits/stdc++.h>
using namespace std;
const int N=40;
int f[N][N]; //f[i][j] 长度为i,第一个数字为j
int dp(int num){
if(num==0) return 1;
vector<int> nums;
while(num) nums.push_back(num%10),num/=10;
int res=0,last=100;
for(int i=nums.size()-1;i>=0;--i){
int x=nums[i];
for(int j=0;j<x;++j){
if(j==0&&(int)nums.size()-1==i){
for(int l=0;l<(int)nums.size();++l){
for(int k=1;k<10;++k) res+=f[l][k];
}
res++;
}
else if(abs(j-last)>=2) res+=f[i+1][j];
}
if(abs(last-x)>=2) last=x;
else break;
if(i==0) res++;
}
//cout<<res<<endl;
return res;
}
int main(){
for(int i=0;i<10;++i) f[1][i]=1;
for(int i=2;i<N;++i){
for(int j=0;j<10;++j){
for(int k=0;k<10;++k){
if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
}
}
}
//cout<<f[2][0]<<endl;
int l,r;
cin>>l>>r;
cout<<dp(r)-dp(l-1)<<endl;
return 0;
}