关于数位DP的两种写法
  • 板块学术版
  • 楼主Falling_Sakura
  • 当前回复2
  • 已保存回复2
  • 发布时间2023/10/16 19:53
  • 上次更新2023/11/2 13:24:29
查看原帖
关于数位DP的两种写法
773503
Falling_Sakura楼主2023/10/16 19:53

这里以 P4999为例,

状态 fi,1/0f_{i,1/0} 代表从高到低填到第 ii 位了当前已经填的这些位是等于 xx 的这些位还是小于 xx 的这些位,这种情况下的方案数。

gg 代表数位之和。

状态 fpos,sumf_{pos,sum} 代表位置 [pos+1,len][pos+1,len] 已经填完了,这些数位之和在 sumsum 的情况下,数位 [1,pos][1,pos] 无 limitlimit 的情况下,所有数的数位之和。

递归式:

LL dfs(int pos, bool limit, int sum)
{
    if(!pos) return sum;
    if(!limit && ~f[pos][sum])
        return f[pos][sum];
    int up = limit ? a[pos] : 9; // 上限
    LL res = 0;
    for(int i = 0; i <= up; i++)
        res = (res + dfs(pos - 1, limit && i == up, sum + i)) % MOD;
    if(!limit)
        f[pos][sum] = res;
    return res;
}
LL solve(LL x)
{
    int len = 0;
    while(x)
    {
        a[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, true, 0);
}

递推式:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;  
const int MOD = 1e9 + 7;

int a[22];
LL f[22][2];
LL g[22][2];

LL solve(LL x)
{
    memset(a, 0, sizeof(a));
    memset(f, 0, sizeof(f));
    memset(g, 0, sizeof(g));
    int len = 0;
    while(x)
    {
        a[++len] = x % 10;
        x /= 10;
    }
    f[len + 1][1] = 1;
    for(int i = len + 1; i >= 2; i--)
        for(int j = 0; j <= 1; j++)
            for(int r = 0; r <= 9; r++)
            {
                if(j == 1 && r > a[i - 1]) continue;
                f[i - 1][j && (r == a[i - 1])] = (f[i][j] + f[i - 1][j && (r == a[i - 1])]) % MOD;
                g[i - 1][j && (r == a[i - 1])] = (g[i - 1][j && (r == a[i - 1])] + g[i][j] + f[i][j] * r) % MOD;
            }
    return (g[1][0] + g[1][1]) % MOD;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        LL l, r;
        scanf("%lld%lld", &l, &r);
        LL ans = (solve(r) - solve(l - 1) + MOD) % MOD;
        cout << ans << endl;
    }
    return 0;
}

两种写法的时间复杂度是一样的,但是递推是从高位往低位递推,低位借助高位的信息实现状态转移; 而递归是通过记录已经搜索过的后缀信息,实现高位到低位的跨越,相当于先处理出低位再转移到高位。 根据多叉树的结构,记录前缀状态所花费的空间明显比记录后缀所花费的空间要少,假如记录的状态值域大一点的话就有爆空间的风险。

而递归式的写法比递推式的写法思维难度要低一些。

所以孰优孰劣?

2023/10/16 19:53
加载中...