您的位置:首页 > 其它

hdu 3555 Bomb(数位dp)

2014-11-01 22:52 274 查看
#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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: