数位DP问题

数位DP问题

常用的数位DP模板(转)

int dfs(int i, int s, bool e) {
    if (i==-1) return s==target_s;
    if (!e && ~f[i][s]) return f[i][s];
    int res = 0;
    int u = e?num[i]:9;
    for (int d = first?1:0; d <= u; ++d)
        res += dfs(i-1, new_s(s, d), e&&d==u);
    return e?res:f[i][s]=res;
}

其中:

f为记忆化数组;

i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);

s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);

e表示之前的数是否是上界的前缀(即后面的数能否任意填)。

for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.

于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。

注意:

不满足区间减法性质的话(如hdu 4376),不能用solve(r)-solve(l-1),状态设计会更加诡异。

刷题目录:

hdu2089 不要62

Hdu3555不能出现连续的49

HDU 3652 出现13,而且能被13整除。

HDU 3709 平衡数

light OJ 1140两个数之间的所有数中零的个数。

lightoj 1032 二进制数中连续两个‘1’出现次数的和

Codeforces 55D. Beautiful numbers

POJ 3252 Round Number (组合数)

LightOJ 1068 能被K整数且各位数字之和也能被K整除的数

LightOJ1205求区间[a,b]的回文数个数。

hdu3886求满足符号串的数字个数。

HDU4352严格上升子序列的长度为K的个数。

ural 1057 数位统计

codeforces215E周期数

codeforces258B在1-m中任选7个数,要使前六个数字中的“4”,”7”之和小于第七个的,

HDU4507 和7无关数的平方和

Zoj2599 数位统计(见题意)

zoj3162分形、自相似

ZOJ3494 BCD Code(AC自动机+数位DP)

不要62

题目传送:HDU - 2089 - 不要62

AC代码:

/* Created Time: 2015/10/28 21:20:03 */
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int bit[10];

int dp[10][2];//dp[len][0/1]表示不含4和62的前提下,剩余长度为len,首位是否为6的个数。

//dfs从高位到低位依次枚举每一位
int dfs(int len, bool state, bool mx) {//len表示剩余长度, state表示之前一个位置是否放了6,mx表示之前的数是否是上界的前缀(即后面的数能否任意填)。
    if(len == 0) return 1;
    if(!mx && dp[len][state] != -1) return dp[len][state];
    int ret = 0;
    int u = mx ? bit[len] : 9;
    for(int i =  0; i <= u; i ++) {
        if(i == 4 || state && i == 2) continue;//不算含有4和62的情况
        ret += dfs(len - 1, i == 6, mx && i == u);
    }
    if(!mx) dp[len][state] = ret;
    return ret; 
}

int get(int n) {
    int len = 0;
    while(n) {
        bit[++ len] = n % 10;
        n /= 10;
    }
    return dfs(len, false, true);
}

int main() {
    int n, m;
    memset(dp, -1, sizeof(dp));
    while(scanf("%d %d", &n, &m) != EOF) {
        if(n == 0 && m == 0) break;
        printf("%d\n", get(m) - get(n - 1));
    }
    return 0;
}


Bomb

题目传送:HDU - 3555 - Bomb

AC代码:

/* Created Time: 2015/10/28 22:05:51 */
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

LL n;
int bit[25];
LL dp[25][2];//dp[len][0/1]表示不含49的数字,剩余长度为len,首位是否为4的个数

LL dfs(int len, bool state, bool mx) {//state=true表示之前选了4
    if(len == 0) return 1;
    if(!mx && dp[len][state] != -1) return dp[len][state];
    LL ret = 0;
    int u = mx ? bit[len] : 9;
    for(int i = 0; i <= u; i ++) {
        if(state && i == 9) continue;
        ret += dfs(len - 1, i == 4, mx && i == u);
    }
    if(!mx) dp[len][state] = ret;
    return ret;
}

LL get_ans(LL x) {
    int len = 0;
    LL m = x;
    while(x) {
        bit[++ len] = x % 10;
        x /= 10;
    }
    return m - dfs(len, false, true);
}

int main() {
    int T;
    scanf("%d", &T);
    memset(dp, -1, sizeof(dp));
    while(T --) {
        scanf("%I64d", &n);
        printf("%I64d\n", get_ans(n) + 1);
    }
    return 0;
}


你可能感兴趣的:(dp,ACM,DFS,数位dp)