按照题目要求求一个区间满足条件的数
例如:求【1,1000000000000】里带有49的数的个数。
暴力?
枚举+检查=挂掉 毫无疑问
构造+dfs也许能行,但裸的必爆
解法,按位dp (数位dp)
数位DP是一种构造思想,题目怎么要求我们就怎么找,一般从高位向低位填充。 因为在填充的过程中,某些满足条件的已经被计算出来了,那么就会出现重复子问题,利用记忆化搜索就可以节省大量时间和和空间。
以这道题为例:
Description
The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point.
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Input
The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description.
The input terminates by end of file marker.
The input terminates by end of file marker.
Output
For each test case, output an integer indicating the final points of the power.
Sample Input
3 1 50 500
Sample Output
0 1 15
Hint
From 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499", so the answer is 15.
要求带有49的个数
这么考虑,从高位往下填充,当前一位是4的时候,这一位填9就满足条件了;当之前满足了出现49,之后不一定出现49但也可以。
所以从高位往下填充
int dfs(int n,bool a,bool c,bool p) //n表示当前填的位数 a表示是否越界(默认越界) c表示是否满足条件 p表示上一位是否是4
当n<0时,我们已经填完了,只需检查一下c是否满足
然后,如果没有越界,下一位可填范围为0-9,否则只能到当前数字最大。
枚举,把填了之后的数字累加。
long long dfs(long long n,bool a,bool c,bool p)//c满足条件
{
if(n<0)
{
if(c)return 1;
else return 0;
}
if(dp[n][a][c][p])return dp[n][a][c][p];
long long ed;
if(a)ed=9;
else ed=pos[n];
for(long long i=0;i<=ed;i++)
{
dp[n][a][c][p]+=dfs(n-1,a||i<pos[n],c||(p&&i==9),i==4);
}
return dp[n][a][c][p];
}
简而言之,数位dp是求满足条件的数,大致是一种dp的思想(毕竟是记忆化)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
long long dp[65][2][2][2];
long long pos[65];
long long dfs(long long n,bool a,bool c,bool p)//c满足条件
{
if(n<0)
{
if(c)return 1;
else return 0;
}
if(dp[n][a][c][p])return dp[n][a][c][p];
long long ed;
if(a)ed=9;
else ed=pos[n];
for(long long i=0;i<=ed;i++)
{
dp[n][a][c][p]+=dfs(n-1,a||i<pos[n],c||(p&&i==9),i==4);
}
return dp[n][a][c][p];
}
int main()
{
long long T;
scanf("%I64d",&T);
for(long long i=1;i<=T;i++)
{
memset(dp,0,sizeof(dp));
memset(pos,0,sizeof(pos));
long long n;
scanf("%I64d",&n);
long long len=0;
for(len=0;;len++)
{
if(n)
{
pos[len]=n%10;
n/=10;
}
else break;
}
printf("%I64d\n",dfs(len,0,0,0));
}
return 0;
}