HDU 4734 F(x) 数位dp
来源:互联网 发布:软件注册授权系统 编辑:程序博客网 时间:2024/06/04 19:48
定义状态dp[i][j][m]表示以j开头的i位十进制数字中,函数值小于等于m的数字的个数。
有如下状态转移方程
代码如下:
#include <algorithm>#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>using namespace std;int A, B, bit[15];int dp[15][15][5005];int digit[15], len;void init(){ memset(dp, 0, sizeof(dp)); for (int i = 0; i <= 9; i++) for (int j = i; j <= 5000; j++) dp[1][i][j] = 1; for (int i = 2; i <= 10; i++) for (int j = 0; j <= 9; j++) for (int m = 0; m <= 5000; m++) for (int k = 0; k <= 9; k++) if (m >= j * bit[i]) dp[i][j][m] += dp[i - 1][k][m - j * bit[i]];}int cal(int n){ memset(digit, 0, sizeof(digit)); int len = 0; while (n) { digit[++len] = n % 10; n /= 10; } int ans = 0, pre = 0; for (int i = len; i >= 1; i--) { for (int j = 0; j < digit[i]; j++) ans += dp[i][j][A - pre]; pre += digit[i] * bit[i]; if (pre > A) break; } return ans;}int main(){ //freopen("test.txt", "r", stdin); int T, Case = 1; scanf("%d", &T); bit[1] = 1; for (int i = 2; i <= 10; i++) bit[i] = bit[i - 1] * 2; init(); while (T--) { scanf("%d%d", &A, &B); int temp[15], len = 0; while (A) { temp[++len] = A % 10; A /= 10; } for (int i = 1; i <= len; i++) A += temp[i] * bit[i]; printf("Case #%d: ", Case++); printf("%d\n", cal(B + 1)); } return 0;}
通过写这道题目,我发现dfs写法虽然看起来简洁,可是要考虑的东西要更多。
一开始我是每次都dfs一遍,最后超时。看了大牛的博客才发现只要把状态稍稍改一下,dp数组就可以只计算一次。
具体是这样的,如果dp[pos][sum]中的sum表示的是前缀和的话,dfs的临界条件应该是return sum <= A。通过这个临界条件很容易知道这种dp是和输入的A有直接关系的,所以每次dfs都必须将dp数组memset,这样一来无法使用上一次dp的信息。
但是如果dp[pos][sum]中的sum表示的是最多还能凑的数,也就是F(A) - prefix(前缀)。dfs的临界条件就变成了 return sum >= 0。通过这个临界条件很容易知道这种dp是和输入的A无关的,我们只需要在第一次dp的时候memset即可,以后的dp可以直接使用以前的数据。
代码如下:
#include <algorithm>#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>using namespace std;int dp[15][5005];int digit[10], bit[15];int A, B;int dfs(int pos, int val, int limit){ if (!pos) return val >= 0; if (val < 0) return 0; if (!limit && dp[pos][val] != -1) return dp[pos][val]; int up = limit ? digit[pos] : 9; int ans = 0; for (int i = 0; i <= up; i++) ans += dfs(pos - 1, val - i * bit[pos], limit && i == up); return limit ? ans : dp[pos][val] = ans;}int cal(int n){ int len = 0; while (n) { digit[++len] = n % 10; n /= 10; } return dfs(len, A, 1);}int main(){//freopen("test.txt", "r", stdin); bit[1] = 1; for (int i = 2; i <= 10; i++) bit[i] = bit[i - 1] * 2; int T, Case = 1; scanf("%d", &T); memset(dp, -1, sizeof(dp)); while (T--) { scanf("%d%d", &A, &B); int temp[15], len = 0; while (A) { temp[++len] = A % 10; A /= 10; } A = 0; for (int i = len; i >= 1; i--) A = A * 2 + temp[i]; printf("Case #%d: ", Case++); printf("%d\n", cal(B)); }return 0;}
阅读全文
0 0
- [HDU 4734]F(x)[数位DP]
- hdu 4734 F(x)(数位DP)
- 简单数位dp-hdu-4734-F(x)
- 【数位DP】 HDU 4734 F(x)
- hdu-4734-F(x)--数位dp
- hdu 4734 F(x) --- 数位dp
- hdu 4734 F(x) (数位dp)
- [数位dp] hdu 4734 F(x)
- hdu 4734 F(x)(数位dp)
- 【HDU】4734 F(x) 数位DP
- HDU 4734 F(x)(数位DP)
- HDU 4734 F(x) (数位DP)
- hdu 4734 F(x) 数位dp
- 【数位DP】 HDU 4734 F(x)
- HDU 4734 F(x)(数位DP)
- HDU 4734 F(X) 数位DP
- Hdu 4734 F(x) (数位dp)
- HDU 4734 F(x) (数位DP)
- jquery 实现 点击按钮后倒计时效果,多用于实现发送手机验证码、邮箱验证码
- HBase中RegionServer Split的过程(翻译)
- Fragment的setUserVisibleHint方法实现懒加载
- CornerStone 常见的图标含义
- 理解e.clientX、e.clientY、e.pageX、e.pageY、e.offsetX、e.offsetY、e.screenX、e.screenY
- HDU 4734 F(x) 数位dp
- 合并两个排序的链表
- Resin4和Tomcat8容器下部署Maven项目
- 树的子结构
- 谈谈iOS中图片的解压缩
- rsyslogd、syslog远程传输、日志存储、转存
- 关于javaScript中onmousemove事件没效果的原因解释
- 二叉树的镜像
- Openstack Ocata安装(Block Storage service)