模板 - 动态规划 - 数位dp

二进制的某道题。

int a[40];
ll dp[40][40][40];
ll dfs(int pos, int s1, int s2, bool lead, bool limit) {
    if(pos == -1) {
        if(s1 >= s2)
            return 1;
        else
            return 0;
    }
    if(!limit && !lead && dp[pos][s1][s2] != -1)
        return dp[pos][s1][s2];
    int up = limit ? a[pos] : 1;
    ll ans = 0;
    for(int i = 0; i <= up; i++) {
        int ns1 = s1 + ((!lead) && i == 0);
        int ns2 = s2 + (i == 1);
        ans += dfs(pos - 1, ns1, ns2, lead && i == 0, limit && i == a[pos]);
    }
    if(!limit && !lead)
        dp[pos][s1][s2] = ans;
    return ans;
}

ll solve(ll x) {
    if(x < 0)
        return 0;
    int pos = 0;
    while(x) {
        a[pos++] = x % 2;
        x /= 2;
    }
    return dfs(pos - 1, 0, 0, true, true);
}

void test_case() {
    memset(dp, -1, sizeof(dp));
    ll L, R;
    scanf("%lld%lld", &L, &R);
    printf("%lld\n", solve(R) - solve(L - 1));
}

十进制的某道题,统计[L,R]中,[0,9]的数字的出现次数,这个需要注意很多细节。比如当lead标记存在时不计算0(即使就是0也不计算)。当limit标记存在且i=d[pos]时,说明这一位取i=d[pos]之后,剩下的数字有r[pos-1]+1种,其中数组r表示原数字模10的幂次的余数(也就是十进制下的低位的数,例如:123456,当取到123???时,???的范围就是[000,456],此时对应的r值就是456,当取到122???时,解除了限制,???的范围就是[000,999])。

int k;
ll p10[17];

int d[17];
ll r[17];
ll dp[17];

ll dfs(int pos, bool lead, bool limit) {
    if(pos == 0)
        return 0;
    if(!lead && !limit && dp[pos] != -1)
        return dp[pos];
    int up = limit ? d[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++) {
        ll D = dfs(pos - 1, lead && i == 0, limit && i == d[pos]);
        ans += D;
        if(i == k) {
            if(lead && i == 0)
                continue;
            if(limit && i == d[pos])
                ans += r[pos - 1] + 1;
            else
                ans += p10[pos - 1];
        }
    }
    if(!lead && !limit)
        dp[pos] = ans;
    return ans;
}

ll solve(ll x) {
    if(x < 0)
        return 0;
    int pos = 0;
    ll cx = x;
    while(cx) {
        d[++pos] = cx % 10;
        cx /= 10;
    }
    for(int i = 1; i <= pos; ++i)
        r[i] = x % p10[i];
    return dfs(pos, true, true);
}

int cost[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};

void test_case() {
    p10[0] = 1;
    for(int i = 1; i < 17; ++i)
        p10[i] = p10[i - 1] * 10;
    ll L, R;
    scanf("%lld%lld", &L, &R);
    ll sum = 0;
    for(k = 0; k <= 9; ++k) {
        memset(dp, -1, sizeof(dp));
        ll cnt = solve(R) - solve(L - 1);
        sum += cnt * cost[k];
    }
    printf("%lld\n", sum);
}
posted @ 2020-03-13 14:14  KisekiPurin2019  阅读(197)  评论(0编辑  收藏  举报