不要62
限制条件:不能有4,也不能有连续的62
dp[pos][sta]代表枚举到第pos位,sta=0或1,代表前一位是否是6
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
int a[20];
int dp[20][2];
int dfs(int pos,int pre,int sta,bool limit){
if(pos==-1) return 1;
if(!limit && dp[pos][sta]!=-1)return dp[pos][sta];
int up=limit?a[pos]:9; //上界要么是9,要么是这一位的数
int ans=0;
for(int i=0;i<=up;i++){
//开始枚举这一位的数
if(pre==6 && i==2)continue; //不要62
if(i==4)continue; //不要4
ans+=dfs(pos-1,i,i==6,limit && i==a[pos]);
}
if(!limit) dp[pos][sta]=ans;
return ans;
}
int solve(int x){
int pos=0;
while(x){
a[pos++]=x%10;
x/=10;
}
return dfs(pos-1,-1,0,true);
}
int main(){
int l,r;
while(cin>>l>>r){
memset(dp,-1,sizeof(dp) );
cout<<(solve(r)-solve(l-1));
}
return 0;
}
Windy数
限制条件:不含前导0且相邻两个数字之差至少为2
1.把求[l,r]转换成求[1,l-1]&[1,r],然后利用类似前缀和的思想
2.把长度短的数字补全前导0
通过以上两步,就把两种限制条件都变成了数位的维度
从高位到低位去枚举---->dfs
dfs都需要记录哪些状态?
1.枚举到了哪一位
2.前一位数字是多少(要保证数位之差>=2)
3.这一位可以填哪些数字
前两个很好维护,我们看第三个参数,以12345为例
由此,对于位置x,可以得出结论:
x填的数有两种可能性
dfs需要参数:pos(当前位数)pre(前一位数字是多少)limit(是否前面每一位都和上限数字相同)
dfs(pos,pre,limit){
if(pos==-1)return 1; //已经枚举完每一位
up=limit?a[pos]:9;
ans =0;
for(int i=0;i<=up,i++){
if(i与pre相差>=2)
ans+=dfs(pos-1,i,limit && i==a[pos] )
}
return ans;
}
加上记忆化如下:
dfs(pos,pre,limit){
if(pos==-1)return 1; //已经枚举完每一位
if(!limit && dp[pos][pre]!=-1)return dp[pos][pre]; //记录过这个状态
up=limit?a[pos]:9;
ans =0;
for(int i=0;i<=up,i++){
if(i与pre相差>=2)
ans+=dfs(pos-1,i,limit && i==a[pos])
}
if(!limit) dp[pos][pre]=ans;
return ans;
}
下面的题解是之前写的,有的变量和写法和上边不一样。
但是可以看看st参数前导0的处理(个人感觉只出现在了两个地方,而且和与它一同出现的limit很像)
#include<iostream>
#include<math.h>
#include<string.h>
using namespace std;
typedef long long ll;
//用dp[pos][pre]代表枚举到第pos位,上一位数是pre的方案总数
ll dp[15][15];
int limit[15]; //记录每个数位上的最高位
ll L,R;
//pos当前位置,pre前一位数,st判断前面是否全是0,flag最高位限制
ll dfs(int pos,int pre,int st,int flag){
if(pos<0) return 1; //枚举结束
//记忆化
if(!flag && !st && dp[pos][pre]!=-1) return dp[pos][pre];
int up=flag?limit[pos]:9;
int ans=0;
for(int i=0;i<=up;i++){
if(abs(i-pre)<2) continue;//不符合题意,继续
if(st&&i==0) ans+=dfs(pos-1,-2,1,flag&&i==up);//如果有前导0,下一位随意
else ans+=dfs(pos-1,i,0,flag&&i==up);//如果没有前导0,继续按部就班地搜
}
if(!flag && !st) dp[pos][pre]=ans; //没有最高位限制和前导0时记录结果
return ans;
}
int solve(ll x){
int pos=0;
//分解数位
while(x){
limit[pos++]=x%10;
x/=10;
}
memset(dp,-1,sizeof(dp));
return dfs(pos-1,-2,1,1);
}
int main(){
cin>>L>>R;
cout<<solve(R)-solve(L-1);
}