题意:给定一个数,让你求从1到这个数的所有数字中含49的数字数量。
析:看到这一个题,首先想到的就是暴力,可是,我一看数的大小就放弃了,2^63-1。。。
还是问学长吧,学长说数位DP,听他说了好久,才明白,好复杂啊,在这里分享一下。
这是我参考的博客网址:http://www.cnblogs.com/liuxueyang/archive/2013/04/14/3020032.html
d[i][0]:表示前 i 位中不含49的数量;
d[i][1]:表示前 i 位中不含49,但是最高位是9(也就是说第 i 位是9),的数量;
d[i][2]:表示前 i 位中含49的数量。
这前 i 位是从低位到高位的。那应该怎么转移呢?
d[i][0] = d[i-1][0] * 10 - d[i-1][1]; 表示长度为 i 的不含有49的数字的个数等于长度为 i - 1 的不含有49的数字的个数*10。
为什么呢?是这样的,d[i-1][0] * 10,这个包含了一种它不应该有的情况,那就是第 i 位是4,而第 i-1 位是9的,所以要减去。
dp[i][1] = dp[i-1][0];表示长度为 i 的并且不含有49同时最高位是9的数字的个数等于,长度为 i - 1 的不含有49的数字的个数,
因为只要在它的高一位加上一个9就可以了。所以是这样。
dp[i][2] = dp[i-1][2] * 10+ dp[i-1][1]; 表示长度为 i 的含有49的数字的个数等于,长度为 i - 1 的数字的个数*10,
再加上长度为 i - 1 的并且不含有49同时最高位是9的数字的个数,为什么呢,这是因为还得加上第 i-1 位是9,而第 i 位是4的情况。
这是预处理一下的。下面是具体计算时的DP。
1.首先加上 i-1位含有49的情况。也就是 ans += d[i-1][2] * a[i];
2.再看看是不是以前出现过49,如果出现了,就再加上现在没有49的情况,即 if(出现过) ans += d[i-1][0] * a[i];
3.如果没有出现过,就要判断这一位是不是大于4,如果大于4,就要再追加上长度为 i - 1 的不含有49但是最高位是9的数字的个数,
因为这个时候可以再这一位填4,肯定能和前组成一个49,因为它是大于4的。即 if(第 i 位大于4 && 没有出现49) ans += d[i-1][1];
4.那就是判断一下有没有49出现,如果有就标记,没有就算了,即 if(出现49了) ok = true;
最后就是在输入的时候,要计算n+1,而不是n,为什么呢?我的个人理解是这样的,如果不加1的话,那么最后一步,只标记了出现49了,
但是并没有把它计算上,所以是n+1。还要注意,这个输入数转化为字符时要把从下标1开始。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <map>
#include <cctype>
#include <cmath>
using namespace std;
typedef long long LL;
const int maxn = 30;
LL d[20][3], a[25];//用 long long
void init(){//先处理一下
memset(d, 0, sizeof(d));
d[0][0] = 1;
for(int i = 1; i <= 20; ++i){
d[i][0] = d[i-1][0] * 10L - d[i-1][1];
d[i][1] = d[i-1][0];
d[i][2] = d[i-1][2] * 10L + d[i-1][1];
}
}
LL solve(LL n){
int len = 0;
while(n){//转化成字符串,当然也可以用sprintf,不过我不怎么用
a[++len] = n % 10L;
n /= 10L;
}
a[len+1] = 0;//清空一下,以免出现巧合
LL ans = 0;
bool ok = false;
for(int i = len; i > 0; --i){
ans += d[i-1][2] * a[i];
if(ok) ans += d[i-1][0] * a[i];//出现过
if(a[i] > 4 && !ok) ans += d[i-1][1];//大于4,并且没有出现过
if(a[i] == 9 && a[i+1] == 4) ok = true;//标记出现过49
}
return ans;
}
int main(){
init();
int T; cin >> T;
LL n;
while(T--){
scanf("%lld", &n);//注意输入输出
printf("%lld\n", solve(n+1));//注意是n+1
}
return 0;
}
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 0x3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline int Min(int a, int b){ return a < b ? a : b; }
inline int Max(int a, int b){ return a > b ? a : b; }
inline LL Min(LL a, LL b){ return a < b ? a : b; }
inline LL Max(LL a, LL b){ return a > b ? a : b; }
inline bool is_in(int r, int c){
return r >= 0 && r < n && c >= 0 && c < m;
}
LL dp[25][2], a[25];
LL dfs(int pos, bool is, bool ok){
if(!pos) return 1;
LL &ans = dp[pos][is];
if(!ok && ans >= 0) return ans;
LL res = 0, n = ok ? a[pos] : 9;
for(int i = 0; i <= n; ++i){
if(is && 9 == i) continue;
res += dfs(pos-1, i == 4, ok && i == n);
}
if(!ok) ans = res;
return res;
}
LL solve(LL n){
int len = 0;
while(n){
a[++len] = n % 10;
n /= 10;
}
return dfs(len, false, true);
}
int main(){
memset(dp, -1, sizeof dp);
int T; cin >> T;
while(T--){
LL x;
scanf("%lld", &x);
printf("%lld\n", x-solve(x)+1);
}
return 0;
}
浙公网安备 33010602011771号