hdu 3555 Bomb(数位dp)

来源:互联网 发布:js正则判断数字长度 编辑:程序博客网 时间:2024/05/15 04:37
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <vector>#include <queue>#include <stack>#include <cassert>#include <algorithm>#include <cmath>#include <set>#include <list>#include <map>#include <limits>using namespace std;#define MIN(a, b) ((a) < (b) ? (a) : (b))#define MAX(a, b) ((a) > (b) ? (a) : (b))#define REP(i, s, t) for(int (i)=(s);(i)<=(t);++(i))#define UREP(i, s, t) for(int (i)=(s);(i)>=(t);--(i))#define REPOK(i, s, t, o) for(int (i)=(s);(i)<=(t) && (o);++(i))#define MAXN 19/***************BEGINHERE*****************/LL f[MAXN+1][10];LL a[MAXN+1];LL t;LL n;LL Count(LL x) {    int cnt = 0;    while(x) {        a[cnt++] = x%10;        x/=10;    }    LL ret = 0;    a[cnt] = -1;    UREP(i, cnt-1, 0) {        REP(j, 0, a[i]-1) {                if (a[i+1] == 4 && j == 9) continue;                ret += f[i+1][j];        }        if (a[i+1] == 4 && a[i] == 9) break;    }    return ret;}int main() {    freopen("input.in", "r", stdin);    cin >> t;    REP(i, 1, 9) f[0][i] = 0;    f[0][0] = 1;    REP(k, 1, 19)        REP(i, 0, 9) {            f[k][i] = 0;            REP(j, 0, 9)                if (!(i == 4 && j == 9)) {                    f[k][i] += f[k-1][j];                }        }    while (t--) {       cin >> n;       cout << n-Count(n+1) + 1 << endl;    }    return 0;}

记忆化搜索:

注意保存状态的时候的维度问题,比如 dp(pos, pre, flag, limit) 的话

下面的代码是错的,比如140,会得到2的结果

原因是,在049的时候保存了状态 (0, 4, 0)

所以在14*的时候发现 (0,4,0) 这个状态已经是1,就直接返回。。

解决方法是,特判 limit=1 的状态 或者 增加维度

LL dp(int pos, int pre, int flag, int limit) {    if (pos < 0) return flag;    if (f[pos][pre][flag] == -1) {        int last = limit ? d[pos] : 9;        LL ret = 0;        for (int i=0;i<=last;++i) {            LL sav = ret;            ret += dp(pos-1, i,                      flag || pre == 4 && i == 9,                      limit && i == last);            if (ret > sav) {                cout << "here: ";                cout << pos << ' ' << pre << ' ' << flag << ' ' << limit << " now: " << i << endl;            }        }        f[pos][pre][flag] = ret;    }    else if (f[pos][pre][flag]) {cout << "bug: ";cout << pos << ' ' << pre << ' ' << flag << ' ' << limit  << endl;}    return f[pos][pre][flag];}

正确的记忆化解法

LL f[22][10][2], d[22], n, len;LL dp(int pos, int pre, int flag, int limit) {    if (pos < 0) return flag;    // 特判 limit 的情况,防止重复计数    if (limit || f[pos][pre][flag] == -1) {        int last = limit ? d[pos] : 9;        LL ret = 0;        for (int i=0;i<=last;++i) {            LL sav = ret;            ret += dp(pos-1, i,                      flag || pre == 4 && i == 9,                      limit && i == last);        }        f[pos][pre][flag] = ret;    }    return f[pos][pre][flag];}LL solve() {    len = 0;    while (n) {d[len++] = n%10;n /= 10;}    memset(f, -1, sizeof(f));    return dp(len-1, 0, 0, 1);}


0 0
原创粉丝点击