【Luogu】每日一题——Day8. P13085 [SCOI2009] windy 数(加强版)(数位DP)

链接:P13085 [SCOI2009] windy 数(加强版) - 洛谷

题目:【Luogu】每日一题——Day8. P13085 [SCOI2009] windy 数(加强版)(数位DP)_第1张图片

思路:

数位DP

看到这种统计符合XX特征的数字时我们就能想到利用数位DP来做

我们通常有两种做法,一种是 DFS+记忆化,另一种则是直接DP预处理所有情况然后统计

这里我们采用 DFS+记忆化 来实现,因为比较简单易懂

我们通常使用 4 个量来递归,now 代表现在是第几位,last 代表上一位我们填了什么,allzero 表示之前是不是全是 0,lim 表示当前位是不是被限制了

DFS代码部分好写,主要是记忆化过程,如何理解记忆化呢?显然我们得搞清楚记忆化的作用:为了防止多次询问同一种状态,那么这里的体现是什么呢?

我们记录 dp[i][j] 表示搜到了第 i 位,且上一位是 j ,同时 lim 位 false 的答案,为什么呢?因为只有我们被限制时才不会重复访问,而没被限制时会多次访问,比如对于 72345

【Luogu】每日一题——Day8. P13085 [SCOI2009] windy 数(加强版)(数位DP)_第2张图片

对于最高位取 1 2,次高位取 1,我们下一个数都能取 1 ~ 9,也就是说我们会多次访问 dp[3][1] 这个状态,即我们现在在第三位,之前一位是 1

这时我们显然要记忆化,那为什么 lim 就不用呢?

因为如果 lim 了,那么只有一种可能,即之前都是 lim,因为只有上一位被限制了,那么这一位才会被限制,这也是为什么 now > len 时直接返回 1,这代表原来的数就是一个符合条件的数,同时这个数不会被记忆化(如 135 这样的数,本就符合题意) 

对于利用DP预处理然后计算的方法放最后了,感兴趣的可以看看别人的代码

代码:

#include 
#include 
#include
#include 
#include
#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl

int a[20];
int len = 0;
//搜到第 i 位,前一位是 j
int dp[20][20];

int dfs(int now,int last,int allzero,int lim)
{
    if (now > len) return 1;
    if (!lim && dp[now][last] != -1)
    {
        return dp[now][last];
    }
    int res = 0;
    int mx = lim ? a[len - now + 1] : 9;
    for (int i = 0; i <= mx; i++)
    {
        if (abs(i - last) < 2) continue;
        if (i == 0 && allzero)
        {
            res += dfs(now + 1, -2, 1, lim && i == mx);
        }
        else
        {
            res += dfs(now + 1, i, 0, lim && i == mx);
        }
    }
    if (!lim && !allzero)
    {
        dp[now][last] = res;
    }
    return res;
}

int query(int x)
{
    len = 0;
    while (x) a[++len] = x % 10, x /= 10;
    memset(dp, -1, sizeof(dp));
    return dfs(1, -2, 1, 1);
}

void solve()
{
    int a, b;
    cin >> a >> b;
    cout << query(b) - query(a - 1);
}
signed main()
{
    //cin.tie(0)->sync_with_stdio(false);
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

DP版本:洛谷 P2657 [SCOI2009] windy 数 _windy数-CSDN博客

你可能感兴趣的:(深度优先,算法,图论)