hdu 3555 Bomb(数位dp)
题意:求1到n有多少个数中含有49,1<=n<=2^63-1(2^32是10位,2^64约20位)
思路:数位dp
dp[i][0],长度为i,不含有49的个数
dp[i][1],长度为i,不含有49,最高位为9的个数
dp[i][2],长度为i,含有49的个数
状态转移方程:
dp[i][0]=10*dp[i-1][0]-dp[i-1][1];//不含49的前面加0~9,减掉9前面加4
dp[i][1]=dp[i-1][0];//不含49的前面加9
dp[i][2]=10*dp[i-1][2]+dp[i-1][1];//含49的前面加0~9,加上9前面加4
求解:从最高位开始遍历,每一位求的都是 前缀+小于当前位 的符合条件的个数。
1.首先加上当前位置之后包含49的个数,因为当前为可以填0~bit[i]-1,所以乘以bit[i],ans+=dp[i-1][2]*bit[i];//注意这个位置,求的是前缀+当前位置
2.前面的前缀出现49了,ans+=dp[i-1][0]*bit[i];//
3.前缀没有出现49,并且当前位>4,ans+=dp[i-1][1];
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> using namespace std; long long dp[25][3]; /* dp[i][0],不含有49 dp[i][1],不含有49,最高位为9 dp[i][2],含有49 */ void init(){ dp[0][0]=1; dp[0][1]=dp[0][2]=0; int i; for(i=1;i<25;++i){ dp[i][0]=10*dp[i-1][0]-dp[i-1][1];//不含49的前面加0~9,减掉9前面加4 dp[i][1]=dp[i-1][0];//不含49的前面加9 dp[i][2]=10*dp[i-1][2]+dp[i-1][1];//含49的前面加0~9,加上9前面加4 } } int bit[25]; long long calc(long long n){ int len=0,i; while(n){ bit[++len]=n%10; n/=10; } bit[len+1]=0; bool flag=false; long long ans=0; for(i=len;i>=1;--i){ ans+=dp[i-1][2]*bit[i];//注意这个位置,求的是前缀+当前位置 if(flag)ans+=dp[i-1][0]*bit[i]; else if(bit[i]>4)ans+=dp[i-1][1]; if(bit[i+1]==4&&bit[i]==9)flag=true; } if(flag)++ans;//加上n本身 return ans; } int main(){ init(); int t; long long n; scanf("%d",&t); while(t--){ scanf("%lld",&n); printf("%I64d\n",calc(n)); } return 0; }