hdu5787 数位dp 数位压缩

分析
大概是比较经典的数位dp,需要维护连续的 k 个数字不相同,所以我们的状态记录里需要记录前k1个数字是哪些,扩展下一位的时候不能出现前 k1 位的数字。
这样就来设计状态: dp[len][k][ban] ,表示前长度为 len ,连续的 k 个字符禁止出现相同,前k1个数字为 ban
这里的 ban 用了数字压缩的技巧,比如前 3 个数字为4,2,5,那么我的 ban 就是10进制数 425
到这里赛场上都很容易想到,但是由于处理前到零一直debug了我 4 个小时才对,还增加了一个维度作为记录连续的多少位内禁止出现0
一言不合就加维度!

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
vector<int> vi;
#define pr(x) cout << #x << ": " << x << "  " 
#define pl(x) cout << #x << ": " << x << endl
#define xx first
#define yy second
#define sa(n) scanf("%d", &(n))
#define rep(i, a, n) for (int i = a; i < n; i++)
#define vep(c) for(decltype((c).begin() ) it = (c).begin(); it != (c).end(); it++) 
#define limit asdf
const int mod = int(1e9) + 7, INF = 0x3fffffff, maxn = 1e5 + 12;
int T;

ll dp[19][4][9876 + 7][5];
int bits[25];
ll l, r;
int k;

ll dfs(int len, int ban, bool limit, int k, int go, int zero, bool pre) {
    if (len == 0) return 1;
    if (!pre && !limit && dp[len][k - 2][ban][zero] != -1) return dp[len][k - 2][ban][zero];
    int m = limit ? bits[len] : 9;
    ll ret = 0;
    bool no[10];
    memset(no, 0, sizeof(no));
    int t = ban;
    for (int i = 0; t; ++i) {
        no[t % 10] = true;
        t /= 10;
    }
    if (!pre && zero > 0) no[0] = true;
    for (int i = 0; i <= m; i++) {
        if (no[i]) continue;
        int nxt = ban % (int)pow(10, k - 2); 
        nxt = nxt * 10 + i;
        int nxtzero = zero;
        if (!pre) {
            if (!i) nxtzero = k - 1;
            else nxtzero--;
            if (nxtzero < 0) nxtzero = 0;
        } 
        ret += dfs(len - 1, nxt, limit && i == m, k, go + 1, nxtzero, pre && !i);
    }
    if (!pre && !limit) dp[len][k - 2][ban][zero] = ret;
    return ret;
}


//常规套路
ll solve(ll n) {
    ll key = n, t = 1;
    while (key) {
        bits[t++] = key % 10;
        key /= 10;
    }
    ll ret = dfs(t - 1, 0, true, k, 0, 0, true);
    return ret;
}


int main(void)
{
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
   // freopen("out.txt", "w", stdout);
#endif
    memset(dp, -1, sizeof(dp));
    while (~scanf("%lld%lld%d", &l, &r, &k)) {
        printf("%lld\n", solve(r) - solve(l - 1));
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值