AcWing 3710:递进数字 ← 最高位到最低位 DFS 枚举 + 南京大学考研机试题

【题目来源】
https://www.acwing.com/problem/content/3713/

【题目描述】
给定两个整数 l,r(l≤r),请问 [l, r] 范围内,满足数字的任意相邻两位差值都恰好为 1,且数字至少有两位的数有多少个。

【输入格式】
第一行包含整数 T,表示共有 T 组测试数据。
每组数据占一行,包含两个整数 l 和 r。

【输出格式】
每组数据输出一行,一个结果。

【输入样例】
14 2023

【输出样例】
1
17

【数据范围】
1≤T≤100,
0≤l≤r≤3×10^8

【算法分析】
● 从最高位到最低位枚举,最高位有 1~9 共 9 种选择(最高位不能为 0)。其后的每一位,因为要满足“任意相邻两位差值都恰好为 1”的约束条件,所以,最高位之后的每一位的值,只有两种选择。鉴于题目给出的最大数为 3×10^8,故总的选择数为 9×2^8=2304。

● 也就是说,从最高位到最低位枚举求解,不会超时(TLE)。

● 下面代码中,函数 dfs 的各个形参含义:u表示当前数的位数,x表示当前数的值,pre记录上一位数的值

【算法代码:】

#include <bits/stdc++.h>
using namespace std;

int le,ri,cnt;
int T;

//u表示当前数的位数,x表示当前数的值,pre记录上一位数的值
void dfs(int u,int x,int pre) {
    if(x>ri) return;
    if(u>1 && x>=le && x<=ri) cnt++;

    if(u==0) {
        for(int i=1; i<10; i++) dfs(u+1,x*10+i,i);
    } else {
        if(pre) dfs(u+1,x*10+pre-1,pre-1);
        if(pre<9) dfs(u+1,x*10+pre+1,pre+1);
    }
}

int main() {
    cin>>T;
    while(T--) {
        cnt=0;
        cin>>le>>ri;
        dfs(0,0,-1);
        cout<<cnt<<endl;
    }
    return 0;
}

/*
in:
4
1 10
10 10
1 100
0 300000000

out:
1
1
17
1992
*/

【算法代码:数位DP】→ https://blog.csdn.net/hnjzsyjyj/article/details/156294090

#include <bits/stdc++.h>
using namespace std;

const int N=12;
int f[N][10]; //f[i][j]表示长度为i最高位为j的满足条件的数字个数

void init() {
    for(int i=0; i<=9; i++) f[1][i]=1;
    for(int i=2; i<N; i++) {
        for(int j=0; j<=9; j++) {
            if(j==0) f[i][j]=f[i-1][1];
            else if(j==9) f[i][j]=f[i-1][8];
            else f[i][j]=f[i-1][j-1]+f[i-1][j+1];
        }
    }
}

int dp(int n) {
    if(n<=0) return 0; //vip
    vector<int> v;
    while(n) {
        v.push_back(n%10);
        n/=10;
    }

    int cnt=0,pre=-1;
    for(int i=v.size()-1; v.size()>=2 && i>=0; i--) {
        int x=v[i];
        int start=(i==v.size()-1)?1:0;
        for(int j=start; j<x; j++) {
            if(pre==-1 || abs(pre-j)==1) {
                cnt+=f[i+1][j];
            }
        }

        if(abs(pre-x)==1 || pre==-1) pre=x;
        else break;

        if(i==0) cnt++;
    }

    for(int i=2; i<=v.size()-1; i++) {
        for(int j=1; j<=9; j++) cnt+=f[i][j];
    }
    return cnt;
}

int main() {
    init();
    int T,le,ri;
    cin>>T;
    while(T--) {
        cin>>le>>ri;
        cout<<dp(ri)-dp(le-1)<<endl;
    }
}

/*
in:
4
1 10
10 10
1 100
0 300000000

out:
1
1
17
1992
*/





【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/156294090
https://blog.csdn.net/hnjzsyjyj/article/details/156048397
https://blog.csdn.net/hnjzsyjyj/article/details/156039366
https://blog.csdn.net/hnjzsyjyj/article/details/156267002
https://blog.csdn.net/hnjzsyjyj/article/details/156011817
https://blog.csdn.net/WhereIsHeroFrom/article/details/148437243
https://www.acwing.com/solution/content/245183/




 

 

posted @ 2025-12-26 14:32  Triwa  阅读(40)  评论(0)    收藏  举报