首页 > 其他分享 >数位dp

数位dp

时间:2024-04-07 11:25:54浏览次数:15  
标签:数字 49 个数 长度 include dp 数位

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555

题目大意:

  给一个数字n,范围在1~2^63-1,求1~n之间含有49的数字有多少个。

思路:

  经典的数位DP,学习了一下,看的别人的代码:http://www.cnblogs.com/luyi0619/archive/2011/04/29/2033117.html

  状态转移:

  dp[i][0]代表长度为 i 并且不含有49的数字的个数;

  dp[i][1]代表长度为 i 并且不含有49,但是最高位是9的数字的个数;

  dp[i][2]代表长度为 i 并且含有49的数字的个数。

  数组 a[i] 从低位到高位存储 n 的每一位数字。

  则:dp[i][0] = dp[i-1][0] * a[i] - dp[i-1][1];  表示长度为 i 的不含有49的数字的个数等于长度为 i - 1 的不含有49的数字的个数*当前的数字,因为这个位置可以填0~a[i] - 1,然后再减去长度为 i - 1 的最高位是9的数字的个数,因为如果长度为 i - 1 的最高位是9的话,那么高一位就不能填4了,否则就组成了49。

    dp[i][1] = dp[i-1][0]; 表示长度为 i 的并且不含有49同时最高位是9的数字的个数等于,长度为 i - 1 的不含有49的数字的个数,因为只要在它的高一位加上一个9就可以了。

    dp[i][2] = dp[i-1][2] * a[i] + dp[i-1][1]; 表示长度为 i 的含有49的数字的个数等于,长度为 i - 1 的数字的个数*当前的数字,再加上长度为 i - 1 的并且不含有49同时最高位是9的数字的个数,因为这个时候,只要在高一位加上一个4就可以了,这样在最高的两位就组成了一个49。

  做法是从数字的高位向低位扫描,对于第 i 位,

  1.   首先加上长度为 i - 1 的符合条件的数字个数;
  2.   再讨论以前是不是出现过49,如果出现过,就要再追加上长度为 i - 1 的不符合条件的数字的个数,因为以前已经有49了;
  3.   如果没有出现过,就要判断这一位是不是大于4呢,如果大于4,就要再追加上长度为 i - 1 的不含有49但是最高位是9的数字的个数,因为这个时候可以再这一位填4,因为它大于4嘛~;
  4.   然后就是判断一下,当前位和上一位是不是满足49,如果满足,标记出现了49了!为以后的判断做准备。

  其实这个题目还有一个地方不懂,就是为什么要在输入 n 后,要把 n 加1。想了一下特例,比如输入49,按照上面的做法,在第3步,并不会把符合条件的数字加上,因为4不是严格大于4,最后的执行结果就是0,但是如果加上1之后,n就变成了50,这样第3步恰好可以执行,结果就是正确的了。但是对于一般的情况,还是不知道为什么要把n加1……o(╯□╰)o

  这题还是卡了很久,照着别人的代码敲的,死活过不了,然后又找了一份代码:http://blog.csdn.net/acm_cxlove/article/details/7819907 才发现输入输出要用%I64d,这不是坑么……原来hdu要用%I64d,囧……

  所以,有时候的bug不是算法或者代码有错误,看看你的输入输出吧!还有,类似的情况,比如,输入文件写错了……更悲剧了。。

复制代码
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cctype>
 6 #include <stack>
 7 #include <queue>
 8 #include <cmath>
 9 #include <algorithm>
10 #define lson l, m, rt<<1
11 #define rson m+1, r, rt<<1|1
12 using namespace std;
13 typedef long long int LL;
14 const int MAXN =  0x3f3f3f3f;
15 const int  MIN =  -0x3f3f3f3f;
16 const double eps = 1e-9;
17 const int dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,1},
18   {1,1},{1,-1},{-1,-1}};
19 LL dp[21][3]; unsigned long long int n; int a[25];
20 int main(void){
21 #ifndef ONLINE_JUDGE
22   freopen("hdu3555.in", "r", stdin);
23 #endif
24   int t; scanf("%d", &t);
25   memset(dp, 0, sizeof(dp));
26   dp[0][0] = 1;
27   for (int i = 1; i < 21; ++i){
28     dp[i][0] = dp[i-1][0] * 10 - dp[i-1][1];
29     dp[i][1] = dp[i-1][0];
30     dp[i][2] = dp[i-1][2] * 10 + dp[i-1][1];
31   }
32   while (t--){
33     //cin >> n;
34     scanf("%I64d", &n);
35     int len = 0; memset(a, 0, sizeof(a));
36     n++;
37     while (n){
38       a[++len] = n % 10; n /= 10;
39     } LL ans = 0; int last = 0; bool flag = false;
40     for (int i = len; i >= 1; --i){
41       ans += (dp[i-1][2] * a[i]);
42       if (flag) ans += dp[i-1][0] * a[i];
43       if (!flag && a[i] > 4) {ans += dp[i-1][1];}
44       if (last == 4 && a[i] == 9) {flag = true;}
45       last = a[i];
46     }
47     //cout << ans << endl;
48     printf("%I64d\n", ans);
49   }
50 
51   return 0;
52 }
复制代码

  就为了输入输出,纠结好久……所以有的时候还是用cin,cout当输入量不大的时候……

  还是不懂为什么要把 n 加 1,继续想想,,神牛路过明白的解释一下撒~感激不尽……

  貌似有点儿想明白了……也许加 1 的目的就是处理 n 末两位是49的这种特殊情况,如果最后两位不是49,也不影响结果。比如,如果n是149,变成150,结果是1+1 = 2;如果n是123,变成124,结果依然是1;可是,如果要找的是65这种的呢?需要加5么?如果要统计的是一个三位数呢?比如找1~n里面含有123的数字的个数……该怎么做……

  做了后面几道题目,明白了为什么要n++,因为后面的for循环求的是(0,n)的开区间的符合条件的数字的数目,题目要求[1,n]这个区间内的符合条件的数字的数目,所以要把区间的右端点加1。这样的处理方式比较方便,就不用判断这个端点是不是如何条件的数字了。膜拜神牛的想法……

标签:数字,49,个数,长度,include,dp,数位
From: https://www.cnblogs.com/YJH6994/p/18118682

相关文章

  • 如何进行快速求解大数是否是11的倍数证明(如果奇数位数字和与偶数位数字和的差是11的倍
    当一个数的奇数位上数字和与偶数位上数字和的差是11的倍数时,这个数就是11的倍数。这个性质可以通过数学归纳法和模运算的性质来证明。观察模运算的性质首先,观察到对于任意正整数k,10^k对11取模的结果是循环的:......
  • [笔记]数位dp例题及详解(更新中)
    数位dp的定义引自洛谷日报#84:求出在给定区间\([L,R]\)内,符合条件\(f(i)\)的数\(i\)的个数。条件\(f(i)\)一般与数的大小无关,而与数的组成有关。由于是按位dp,数的大小对复杂度的影响很小。由于数位dp状态的上下文信息比较多,所以一般用记忆化搜索实现,而非递推。P4999烦人的数......
  • socket编程——C++实现基于UDP协议的简单通信(含详解)
    文章后面有代码,可以直接复制在VisualStudio2022中运行(注意:必须是两个项目,客户端服务端各一个,连接在同一网络中,先运行服务端,并且客户端数据发送的目标IP要改为你服务端的IP)目录前言帮助文档一、UDP通信框架1.服务端2.客户端二、服务端实现1.加载库(WSAStartup函数)......
  • 背包DP
    01背包定义dp[i][j]表示从前i件物品中选,体积不超过j的最大价值N,V=map(int,input().split())v=[0]*(N+1)w=[0]*(N+1)foriinrange(1,N+1):v[i],w[i]=map(int,input().split())f=[[0]*(V+1)for_inrange(N+1)]#对于第i件物品,选或......
  • Windows 11 RDP 设置自定义证书
    1.随便生成一个证书或者去freessl之类的地方申请一个证书2.将证书转换成pfx格式opensslpkcs12-export-inkeyprivate_key.key-incertificate.pem-certfileCACert.pem-outcertificate.pfx3.打开certlm右键个人->所有任务->导入,导入刚刚创建的pfx证书......
  • 数位DP
    CF204A题目链接https://codeforces.com/problemset/problem/204/A题目大意模板讲解数位DP模板#include<bits/stdc++.h>#definelllonglongusingnamespacestd;intn,m,t;lll,r;strings;llmemo[20][10][10];lldfs(inti,intfirst,intlast,boolis_limi......
  • Offer必备算法21_回文串dp_六道力扣题详解(由易到难)
    目录①力扣647.回文子串解析代码②力扣5.最长回文子串解析代码③力扣1745.分割回文串IV解析代码④力扣132.分割回文串II解析代码⑤力扣516.最长回文子序列解析代码⑥力扣1312.让字符串成为回文串的最少插入次数解析代码本篇完。①力扣647.回文子串64......
  • flask 装饰器 AssertionError: View function mapping is overwriting an existing en
    1问题描述写了一个登陆认证装饰器,部分试图,只有用户登陆才能访问deflogin_wrapper(func):definner(*args,**kwargs):"""判断是否登陆若是进入视图函数否则重定向到登陆页面"""if......
  • 用UDP协议实现发送接收的网络聊天室
     发送数据 UDP协议是面向无连接的"面向无连接的"通常指的是一种网络通信模式,也称为无连接通信或者数据报通信。在这种模式下,通信的两个端点之间不需要建立持续的连接,而是通过将数据分成小块(数据包)并单独发送来进行通信。每个数据包都包含了足够的信息(如源地址、目标地址......
  • 状压DP
    CF580D题目链接https://codeforces.com/problemset/problem/580/D题目大意思路令dp[i][j]表示,吃菜状态为i,且最后一道菜为j的最大满足感!代码#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintN=20;intn,m,t,q;inta[N],b[N*N][N*......