数位dp小记
来源:互联网 发布:移动网络变成电信 编辑:程序博客网 时间:2024/06/05 12:39
参考:
http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html
kuangbin :http://www.cnblogs.com/kuangbin/category/476047.html
http://blog.csdn.net/cmonkey_cfj/article/details/7798809
http://blog.csdn.net/liuqiyao_01/article/details/9109419
数位dp有递推和记忆化搜索的方法,比较来说记忆化搜索方法更好。通过博客一的一个好的模板,数位dp就几乎变成一个线性dp问题了。
下为博客一内容:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
偷看了下7k+大牛的数位统计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),状态设计会更加诡异。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。正如上面说的要注意:
前导零是否有影响。
是否满足区间减的性质。(如hdu4376,还没做,先记上)
几乎就是模板题:
模板:
UESTC 1307相邻的数差大于等于2
(不允许有前导0,前导0对计算有影响,注意前导0的处理)
int dp[15][10];int bit[15];int dfs(int pos, int s, bool limit, bool fzero){ if (pos == -1) return 1;///前导0的影响!!! if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!! int end = limit ? bit[pos] : 9; int ans = 0; for (int i = 0; i <= end; i++) { if (!fzero && abs(i - s) < 2) continue;///前导0的影响!!! int nows = i; ans += dfs(pos - 1, nows, limit && (i == end), fzero && !i); } return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!}int calc(int n){ if (n == 0) return 1; int len = 0; while (n) { bit[len++] = n % 10; n /= 10; } return dfs(len - 1, 0, 1, 1);}int main (){ memset(dp, -1, sizeof(dp)); int l, r; while (cin >> l >> r) { cout << calc(r) - calc(l - 1) << endl; } return 0;}
Hdu3555不能出现连续的49
int bit[25];LL dp[25][3];/**012*//**pos为当前考虑的位置s为在pos之前已到达的状态limit当前考虑的数字是否有限制,即之前已确定的数是否为n的前缀*/LL dfs(int pos, int s, bool limit){ if (pos == -1) return s == 2; if (!limit && ~dp[pos][s]) return dp[pos][s]; int end = limit ? bit[pos] : 9;///limit选择 LL ans = 0; for (int i = 0; i <= end; i++) { int nows; if (s == 0) { if (i == 4) nows = 1; else nows = 0; } else if (s == 1) { if (i == 9) nows = 2; else if (i == 4) nows = 1; else nows = 0; } else if (s == 2) nows = 2; ans += dfs(pos - 1, nows, limit && i == end); } return limit ? ans : dp[pos][s] = ans;///limit选择}LL calc(LL n){ /// int len = 0; while (n) { bit[len++] = n % 10; n /= 10; } /// return dfs(len - 1, 0, 1);}int main (){ memset(dp, -1, sizeof(dp)); int T; RI(T); LL n; while (T--) { cin >> n; cout << calc(n) << endl; } return 0;}
hdu2089 不要62
int dp[10][3];int bit[10];int dfs(int pos, int s, int limit, bool first){ if (pos == -1) return s == 2; if (!limit && ~dp[pos][s]) return dp[pos][s]; int end = limit ? bit[pos] : 9;// int be = first ? 1 : 0; int ans = 0; for (int i = 0; i <= end; i++) { int nows = s; if (s == 0) { if (i == 6) nows = 1; else if (i == 4) nows = 2; } if (s == 1) { if (i == 2 || i == 4) nows = 2; else if (i == 6) nows = 1; else nows = 0; } if (i == 4) nows = 2; ans += dfs(pos - 1, nows, limit && (i == end), first && !i); } return limit ? ans : dp[pos][s] = ans;}int calc(int n){ int tmp = n; if (n == 0) return 0; int len = 0; while (n) { bit[len++] = n % 10; n /= 10; } return tmp - dfs(len - 1, 0, 1, 1);}int main (){ memset(dp, -1, sizeof(dp)); int l, r;// for (int i = 1; i <= 100; i++)// cout << i << ' ' << calc(i) << endl; while (cin >> l >> r) { if (l + r == 0) break;// cout << calc(r) << ' ' << calc(l - 1) << endl; cout << calc(r) - calc(l - 1) << endl; } return 0;}
UESTC 1307相邻的数差大于等于2
(注意前导0的处理)
int dp[15][10];int bit[15];int dfs(int pos, int s, bool limit, bool fzero){ if (pos == -1) return 1;///前导0的影响!!! if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!! int end = limit ? bit[pos] : 9; int ans = 0; for (int i = 0; i <= end; i++) { if (!fzero && abs(i - s) < 2) continue;///前导0的影响!!! int nows = i; ans += dfs(pos - 1, nows, limit && (i == end), fzero && !i); } return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!}int calc(int n){ if (n == 0) return 1; int len = 0; while (n) { bit[len++] = n % 10; n /= 10; } return dfs(len - 1, 0, 1, 1);}int main (){ memset(dp, -1, sizeof(dp)); int l, r; while (cin >> l >> r) { cout << calc(r) - calc(l - 1) << endl; } return 0;}
POJ 3252 Round Number (组合数)!!!
拆成2进制,在记录0和1的个数
求区间[l,r]中,满足传化成2进制后,0的个数>=1的个数的,数字的个数
int dp[40][40][40];int bit[40];int dfs(int pos, int num0, int num1, bool limit, bool fzero){ if (pos == -1) return num0 >= num1;///前导0的影响!!! if (!limit && !fzero && ~dp[pos][num0][num1]) return dp[pos][num0][num1];///条件判断!!! int end = limit ? bit[pos] : 1; int ans = 0; for (int i = 0; i <= end; i++) { int new0, new1; if (fzero) { new0 = 0, new1 = 0; if (i) new1 = 1; } else { new0 = num0, new1 = num1; if (i) new1++; else new0++; } ans += dfs(pos - 1, new0, new1, limit && (i == end), fzero && !i); } return limit || fzero ? ans : dp[pos][num0][num1] = ans;///条件判断!!!}int calc(int n){ if (n == 0) return 1; int len = 0; while (n) { bit[len++] = n % 2; n /= 2; } return dfs(len - 1, 0, 0, 1, 1);}int main (){ memset(dp, -1, sizeof(dp)); int l, r; while (cin >> l >> r) { cout << calc(r) - calc(l - 1) << endl; } return 0;}
hdu3886求满足符号串的数字个数!!!
统计满足和指定 升降字符串 匹配的个数
int dp[105][105][10];int bit[105];char s[105];char a[105], b[105];int ns;bool ok(int r, int a, int b){ if (s[r] == '/') return a < b; else if (s[r] == '-') return a == b; else if (s[r] == '\\') return a > b;}int dfs(int pos, int r, int pre, bool limit, bool prezero){ if (pos == -1) return (r == ns); if (!limit && !prezero && ~dp[pos][r][pre]) return dp[pos][r][pre]; int end = limit ? bit[pos] : 9; int ans = 0; for (int i = 0; i <= end; i++) { if (prezero) { ans += dfs(pos - 1, r, i, limit && (i == end), prezero && (!i)); } else { if (r < ns && ok(r, pre, i))///优先考虑向后拓展 ans += dfs(pos - 1, r + 1, i, limit && (i == end), 0); else if (r > 0 && ok(r - 1, pre, i)) ans += dfs(pos - 1, r, i, limit && (i == end), 0); } ans %= MOD; } if (!limit && !prezero) dp[pos][r][pre] = ans; return ans;}int calc(char a[], bool dec){ int n = strlen(a); int len = 0, tmp = 0; while (a[tmp] == '0') tmp++; for (int i = n - 1; i >= tmp; i--) { bit[len++] = a[i] - '0'; } if (dec && len > 0) { for (int i = 0; i < len; i++) { if (bit[i]) { bit[i]--; break; } else bit[i] = 9; } } return dfs(len - 1, 0, 0, 1, 1);}int main (){ while (scanf("%s", s) != EOF) { memset(dp, -1, sizeof(dp)); ns = strlen(s); scanf("%s%s", a, b); printf("%08d\n", (calc(b, 0) - calc(a, 1) + MOD) % MOD); } return 0;}
HDU 3709 平衡数
LL dp[20][20][2000];///力矩最大为不超过10*20*10;int bit[20];LL dfs(int pos, int r, int sum, int e){ if (pos == -1) return sum == 0; if (sum < 0) return 0; if (!e && ~dp[pos][r][sum]) return dp[pos][r][sum]; int end = e ? bit[pos] : 9; LL ans = 0; for (int i = 0; i <= end; i++) { ans += dfs(pos - 1, r, sum + i * (pos - r), e && (i == end)); } if (!e) dp[pos][r][sum] = ans; return ans;}LL calc(LL n){ if (n < 0) return 0; int len = 0; while (n) { bit[len++] = n % 10; n /= 10; } LL ans = 0; for (int i = 0; i < len; i++)///需要枚举支点 ans += dfs(len - 1, i, 0, 1); return ans - (len - 1);}int main (){ memset(dp, -1, sizeof(dp)); int t; LL l, r; RI(t); while (t--) { scanf("%I64d%I64d", &l, &r); printf("%I64d\n", calc(r) - calc(l - 1)); } return 0;}
HDU4352严格上升子序列的长度为K的个数。!!!
最长上升子序列结合,通过集合(1<<10)来处理
LL dp[25][1 << 11][11];int bit[25];int k;int getnews(int s, int x){ for(int i=x;i<10;i++) if(s&(1<<i))return (s^(1<<i))|(1<<x); return s|(1<<x);}int getnum(int s){ int ret = 0; while (s) { if (s & 1) ret++; s >>= 1; } return ret;}LL dfs(int pos, int s, int e, int z){ if (pos == -1) return getnum(s) == k; if (!e && ~dp[pos][s][k]) return dp[pos][s][k]; int end = e ? bit[pos] : 9; LL ans = 0; for (int i = 0; i <= end; i++) { int news = getnews(s, i); if (z && !i) news = 0; ans += dfs(pos - 1, news, e && (i == end), z && (!i)); } if (!e) dp[pos][s][k] = ans; return ans;}LL calc(LL n){ int len = 0; while (n) { bit[len++] = n % 10; n /= 10; } return dfs(len - 1, 0, 1, 1);}int main (){ LL n, m; memset(dp, -1, sizeof(dp)); int t; scanf("%d", &t); int nc = 1; while (t--) { cin >> n >> m >> k; printf("Case #%d: ", nc++); cout << calc(m) - calc(n - 1) << endl; } return 0;}
!!!是比较不错,待看的题
比较还好的处理的题目:
Codeforces 55D Beautiful numbers!!!
spoj 10606 Balanced Numbers
ac自动机和数位dp结合(!!!):
hdu4376!!!(区间不可减???)
ZOJ3494 BCD Code(AC自动机+数位DP)!!!
整除和简单统计:
HDU4507 和7无关数的平方和!!!
HDU 3652 出现13,而且能被13整除
LightOJ 1068 能被K整数且各位数字之和也能被K整除的数
light OJ 1140两个数之间的所有数中零的个数。
lightoj 1032 二进制数中连续两个‘1’出现次数的和
其它:
LightOJ1205求区间[a,b]的回文数个数。
ural 1057 数位统计
codeforces215E周期数
codeforces258B在1-m中任选7个数,要使前六个数字中的“4”,"7"之和小于第七个的,
Zoj2599 数位统计(见题意)
zoj3162分形、自相似
- 数位dp小记
- 数位DP小记
- 数位dp学习小记 Hdu 3555
- 数位DP小记 + HDU 2089 不要62
- 数位dp
- 数位DP
- 数位DP
- 数位dp
- 数位dp
- 数位dp
- 数位DP
- 数位dp
- 数位DP
- 【数位DP】
- 数位DP
- 数位dp
- 数位dp
- 数位DP
- Codeforces Beta Round #2_B. The least round way(DP)
- 二分查找
- 寻路包CAINav学习
- uva567 Risk(floyd)
- JSP note
- 数位dp小记
- CSS_样式sample
- 工资查询系统{给一贵族中学开发}
- hdu2553N皇后问题
- Error: Multiple errors reported.\ Failed to execute MI command: -var-create
- PAT A 1025. PAT Ranking (25)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- LeetCode Word Break(DFS)
- leetcode: Reverse Nodes in k-Group