您的位置:首页 > 其它

HDU - 3555 bomb(数位DP) HQG_AC的博客

2018-01-13 14:05 453 查看
http://acm.hdu.edu.cn/showproblem.php?pid=3555 https://vjudge.net/problem/HDU-3555
题意就是求在1~n内含有49的数的个数

dp[i][j][(k)0/1]表示长度为i,首字母为j,1表示存在49的情况,0表示不存在49的情况 
1.先把dp数组搞定
  1°i=1,k=0 ,dp=1 ;
  2°
   for 枚举位数
   for 枚举前一个数
   for 枚举后一个数
     1)j=4 && k=9 只有dp[i][j][1]了,这是+=dp[i-1][k][0/1](都可以,后面的情况无所谓)
     2) else 分别加
2.读入,存数组,置换
3.输答案
  1°长度相同,枚举相同的前缀的长度(从0~s-1(s表示长度,下同))
     1)当前缀为0时,第一个数只能取1,否则程度不相同,不符合,
       ans+=dp[s-i][j][1]
     2)当前面已经拥有49出现时,后面无论去什么都没有问题,+dp[s-i][j][0/1]
       注意:这里有个坑,必须在i=1的情况就判断好当前序列含不含有1,不然在2时再枚举会出奇异错误
     3)普通,+dp[s-i][j][1]
  2°长度不同,枚举长度,再枚举首位.

见代码:

#include <bits/stdc++.h>
using namespace std ;
long long n ;
int b[31];
long long dp[41][41][2]; //dp[i][j][0/1]表示长度为i,首字母为j,1表示存在49的情况,0表示不存在49的情况
int main(){
int T ;
for (int i=0;i<10;i++) dp[1][i][0]=1;
for (int i=2;i<=30;i++){
for (int j=0;j<=9;j++)
for (int k=0;k<=9;k++){
if (j==4 && k==9) { //当前元素含有49
dp[i][j][1]+=(dp[i-1][k][1]+dp[i-1][k][0]) ;
}
else {
dp[i][j][1]+=dp[i-1][k][1] ;
dp[i][j][0]+=dp[i-1][k][0] ;
}
}
}
scanf("%d",&T) ;
for (int e=1;e<=T;e++){
scanf("%lld",&n) ;
memset(b,0,sizeof(b)) ;
int s=0 ;
while(n>0) //b数组表示数字,用字符存
{
b[++s]=n%10 ;
n/=10 ;
}
for(int i=1;i<=s/2;i++) //翻转
{
int t=b[i] ;
b[i]=b[s-i+1] ;
b[s-i+1]=t;
}
long long ans=0 ;int flag=0 ; //flag表示 前面是否已经有49出现
for (int i=0;i<=s;i++) //长度相同,可以无前缀,前缀长度为i
{
if (i==0) //连前缀都没有
{
for (int j=1;j<b[i+1];j++) ans+=dp[s-i][j][1] ;
//前缀之后(前段没有49,有的是2段)这段含49的情况
}
else if (flag) //前几段有49了,那后面有没有都没有关系
{
for (int j=0;j<b[i+1];j++) //这里错了,找了一个小时。因为当前面为49时,i是2。
//这是后就应该符合flag=1这个条件了,所以应该是小于当前b[i+1] ,
//相应的下面flag的判定条件也要变化
{
ans+=(dp[s-i][j][1]+dp[s-i][j][0]) ; //都相加
}
}
else //普通情况
{
for (int j=0;j<b[i+1];j++) ans+=dp[s-i][j][1] ;
}
if (i>=1 && b[i]==4 && b[i+1]==9) flag=1 ; //相应的这里也要改掉
}
if (flag) ans++ ;//该原数符合要求
for (int i=2;i<s;i++) //长度不相同,从2~s枚举(1不可能,s已枚举)
{
for (int k=1;k<=9;k++) //枚举首位元素
ans+=dp[i][k][1] ;
}
printf("%lld\n",ans) ;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: