洛谷 2657 windy 数 (数位dp)

链接 windy 数

题意:
不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。在 a和 b 之间,包括 a 和 b ,总共有多少个 windy 数?

思路:

  1. 数位 dp ,数位 dp 其实就是一种 记忆化搜索,把搜过的状态记录下来,下次再搜索到这个状态可以直接返回值,不需要重复搜索。
  2. dfs 主要记录 了 pos(当前位置) ,pre(前一位),status(是不是有前导0),limit (是否都为最高位)。
  3. 这里不含前导0 的主要意思是 ,如果前面都是 0 那这一位可以从 0 开始,不要满足相邻大于 2.如果不考虑 这个,你前一位填 0 下一位就只能从 2 开始了,会少算很多可行解。具体细节看代码了。
    代码:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll ;
const int maxn=1e6+7;
int dp[15][15][3],a[12];
int dfs(int pos,int pre,int statu,int limit){
    if(pos==-1) return 1;                     //搜索到最后一位了。
    
    if(!limit&&dp[pos][pre+2][statu]!=-1) return dp[pos][pre+2][statu]; //当前状态已将有了,并且前面的位置不都为最高位。
                                                                        //这里 +2 是为了防止越界,所有状态都+2 了对结果是没有影响的
    
    int up=limit ? a[pos] : 9;           //如果前面位置都为 最高位,那这一位只能到 a[pos],否则就是9.
    int tmp=0;
    for(int i=0;i<=up;i++){
        if(abs(i-pre)<2) continue;      // 差大于 2.
        int pree=i;
        if(pree==0&&statu==0) pree=-2; //前面全为0 ,说明这下位可以从 0 开始 所以给它 -2
        tmp+=dfs(pos-1,pree,statu||i!=0,limit&&a[pos]==i); //这里只要 i出现了不为 0 statu 就会变为1,出现了i不等于最高位,接下来的 limit 变成0.
    }
    if(!limit) dp[pos][pre+2][statu]=tmp;
    
    return tmp;
}
int solve(int x){
     int pos=0;
     while(x>0){
         a[pos++]=x%10;
         x/=10;
     }
     return dfs(pos-1,-2,0,1); // pre 从  -2 开始  那第一位可以从 0 开始 
}
int main(){
    memset(dp,-1,sizeof(dp));
    int a,b;
    cin>>a>>b;
    cout<<solve(b)-solve(a-1)<<endl;
}




你可能感兴趣的:(洛谷 2657 windy 数 (数位dp))