数位dp回顾

数位dp回顾_第1张图片
不会数位dp的同学点这里 数位dp教学

#include
using namespace std;
#define ll long long
#define lb long double
#define INF 0x3f3f3f3f
const int maxn = 100035;
const int mod = 1e6 + 7;
int len;
ll dp[15][2][2][12][10];
// dp[i][j][k][l][m]表示当前搜到第i位,且前导0与最高位限制状态为j和k, 且要统计的数字是l, 并且已经产生m个dig的答案
int a[15];
// pos代表当前搜到第几位,lead表示有无前导0, limit表示当前位是有限制, dig表示要统计是的数字, sum表示已经产生的dig的数量
ll dfs(int pos, int lead, int limit, int dig, int sum){
     
    if(pos > len) return sum; // 如果搜完了所有位数则返回答案sum
    if(!limit && !lead && dp[pos][lead][limit][dig][sum] != -1) return dp[pos][lead][limit][dig][sum];
    // 只有在无前导0且当前位无限制的时候才能利用记忆化数组dp来计算答案实现剪枝
    ll res = 0;
    int ret = limit ? a[len-pos+1] : 9; // 根据当前是否有限制来决定当前位最高能选的数字
    //当且仅当当前位有限制且选的数是当前能选的数的上限时下一位会有限制
    for(int i = 0 ; i <= ret ; ++ i){
     
        //如果当前选的数是0且有前导0存在, 那么下一位一定有前导0, 且这是唯一的情况
        if(!i && lead) res += dfs(pos + 1, lead, limit && i == ret, dig, sum);
        //如果当前选的数与目标数字一样,则sum+1
        else if(i == dig) res += dfs(pos + 1, 0, limit && i == ret, dig, sum + 1);
        //剩下的情况sum不变, 继续往下搜
        else res += dfs(pos + 1, 0, limit && i == ret, dig, sum);
    }
    //只有无限制无前导0的情况才更新dp数组;
    return (!limit && !lead) ? dp[pos][lead][limit][dig][sum] = res : res;
}
ll cal(ll x, int y){
      // 将十进制上的数存下来
    len = 0;
    while(x){
     
        a[++len] = x % 10;
        x /= 10;
    }
    memset(dp, -1, sizeof(dp));
    return dfs(1, 1, 1, y, 0); // 从高位向低位搜
}
int main()
{
     
    ll l, r;
    cin >> l >> r;
    for(int i = 0 ; i <= 9 ; ++ i){
     
        cout << cal(r, i) - cal(max(0LL, l - 1), i) << ' ';
    }
    return 0;
}

[HDU2089]不要62

#include
using namespace std;
#define ll long long
#define endl '\n'
const int maxn = 100035;
const int mod = 1e6 + 7;
ll l, r;
int len; int a[16];
ll dp[20][2][2][10];
ll dfs(int pos, int lead, int limit, int last){
     
    if(pos > len) return 1;
    if(!limit && !lead && dp[pos][lead][limit][last] != -1) return dp[pos][lead][limit][last];
    int up = limit ? a[len-pos+1] : 9;
    ll res = 0;
    for(int i = 0 ; i <= up ; ++ i){
     
        if(i == 4) continue;
        if(i == 2 && last == 6) continue;
        if(!i && lead) res += dfs(pos + 1, 1, limit && i == up, i);
        else res += dfs(pos + 1, 0, limit && i == up, i);
    }
    return (!limit && !lead) ? dp[pos][lead][limit][last] = res : res;
}
ll cal(ll x){
     
    len = 0;
    while(x){
     
        a[++len] = x % 10;
        x = x / 10;
    }
    memset(dp, -1, sizeof(dp));
    return dfs(1, 1, 1, 0);
}
int main()
{
     
    while(scanf("%lld %lld", &l, &r) && l && r){
     
        cout << cal(r) - cal(l - 1) << endl;
    }
    return 0;
}

P2657 [SCOI2009]windy数

#include
using namespace std;
#define ll long long
#define endl '\n'
const int maxn = 100035;
const int mod = 1e6 + 7;
ll l, r;
int len; int a[16];
ll dp[20][2][2][10];
ll dfs(int pos, int lead, int limit, int last){
     
    if(pos > len) return 1;
    if(!limit && !lead && dp[pos][lead][limit][last] != -1) return dp[pos][lead][limit][last];
    int up = limit ? a[len-pos+1] : 9;
    ll res = 0;
    for(int i = 0 ; i <= up ; ++ i){
     
        if(abs(i - last) < 2) continue;
        if(!i && lead) res += dfs(pos + 1, 1, limit && i == up, -10);
        else res += dfs(pos + 1, 0, limit && i == up, i);
    }
    return (!limit && !lead) ? dp[pos][lead][limit][last] = res : res;
}
ll cal(ll x){
     
    len = 0;
    while(x){
     
        a[++len] = x % 10;
        x = x / 10;
    }
    memset(dp, -1, sizeof(dp));
    return dfs(1, 1, 1, -10);
}
int main()
{
     
    scanf("%lld %lld", &l, &r);
    cout << cal(r) - cal(l - 1) << endl;
    return 0;
}

你可能感兴趣的:(dp)