zoukankan      html  css  js  c++  java
  • [题解]HDU 3555 Bomb

    啊哈是从CSDN搬过来的啦——(日历上没有下划线不爽)

    传呀传呀传送门
    好久没有写题解了的说QAQ

    原题

    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?

    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.

    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.

    题目描述

    求小于等于n的数中包含49的数有多少

    分析

    首先肯定是想到DP:
    设f[i]表示长度为i的数中有多少包含"49"
    在这里插入图片描述
    比方说n=56789时
    在这里插入图片描述
    假设我们要找的数可以表示为(ar{a_5a_4a_3a_2a_1})也就是说等于(a_5*10000+a_4*1000+a_3*100+a_2*10+a_1),我们考虑来枚举这个数——
    因为它一定比n小,所以(a_5leq 5)

    • (a_5<5)时,也就是(a_5)(0,1,2,3,4)五种取法的时候,后面的(a_4 a_3...)不管怎么放都不会大于n,所以这时答案就可以加上(f[4]*5)
    • (a_5==5)时,继续枚举后面的数,按照相同的方式去看这两个数从那一位开始不同就可以了

    这样看上去仿佛很对
    在这里插入图片描述
    但是我们发现它会比正确答案小,这是为什么呢?
    在这里插入图片描述
    我们发现,当(a_5)取4时,(ar{a_4a_3a_2a_1})(900)居然也是合法的,所以我们还要考虑当现在放了(4)的情况,当放了4时,我们需要再加上以9开头的数的数量
    所以我们改变一下数组

    • (f[i][1])表示长度为i,以9开头的数的数量
    • (f[i][2])表示长度为i,包含"49"的数的数量

    那么,当我们考虑到(a_5)时,要讨论3中情况

    • (a_5<5)(a_5 eq4)时,也就是(a_5)(0,1,2,3) 四种取法的时候,后面的(a_4 a_3...) 不管怎么放都不会大于n,所以这时答案就可以加上(f[4][2]*4)
    • (a_5==4)时,要加上(f[4][2])再加上以9开头的数量(f[4][1]),但是我们发现,像(9049)这些既以9开头又包含"49"的数会被计算到两次,那么还要减去(f[3][2])
    • (a_5==5)时,和上面的相同

    然而问题又来了——
    输入494949发现又一次GG了TAT
    我们考虑n中本身就包含"49"

    在这里插入图片描述
    那么当我们考虑(a_3)时,前面已经包含49了,就不需要再讨论后面有没有包含49,所以下面就是正解了——
    先预处理出三个数组

    • (f[i][0])表示长度为i的数有几个
    • (f[i][1])表示长度为i,不包含"49"的数有几个
    • (f[i][2])表示长度为i,包含"49"的数有几个

    假设原数n为(ar{b_5b_4b_3b_2b_1})从高位向低位扫

    • 如果前面的序列中已经包含"(49)",那么(a_i<b_i),后面的数都可以随便取,对答案的贡献为(f[i-1][0]*b[i])
    • 如果当前(b_ileq4),那么 对于(a_i<b_i),后面的数都可以随便取
    • 如果当前(b_i>4),那么 对于(a_i<b_i),后面也是可以随便取,还有特殊情况(a_i=4),后面还可以取以(9)开头的没有被计算过的数,(ans+=b[i]*f[i-1][2]+f[i-1][1])

    最后要注意细节

    • 比如说当前面包含49时最终答案要加1,因为最后一个数没有被算到
      就是比如n=498时498没有算
    • 再比如说多组数据没有清零就挂了n发

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <string>
    #include <vector>
    #define ll long long
    const int N=100005;
    const int M=200005;
    using namespace std;
    
    ll n,yh;
    int len,flag=0,a[N];
    ll f[N][3],ans=0;
    
    int main(){
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		ans=len=flag=0;yh=1;
    		scanf("%lld",&n);
    		for(int i=1;i<=100;i++){
    			yh*=10;
    			if(yh>n){
    				len=i;
    				break;
    			}
    		}
    		f[0][0]=1;
    		for(int i=1;i<=len;i++){
    			f[i][0]=f[i-1][0]*10;
    			f[i][1]=f[i-1][0]-f[i-1][2];
    			if(i>=2)f[i][2]=f[i-1][2]*10+f[i-1][1];
    		}
    		for(int i=1;i<=len;i++){
    			a[i]=n%10;
    			n/=10;
    		}
    		for(int i=len;i>=1;i--){
    			if(flag){
    				ans+=a[i]*f[i-1][0];
    				continue;
    			}
    			if(a[i]<=4){
    				ans+=a[i]*f[i-1][2];
    			}
    			else{
    				ans+=(a[i]-1)*f[i-1][2];
    				ans+=f[i-1][2]+f[i-1][1];
    			}
    			if(a[i]==9&&a[i+1]==4)flag=1;
    		}
    		if(flag)ans++;
    		printf("%lld
    ",ans);
    		for(int i=1;i<=len;i++)f[i][0]=f[i][1]=f[i][2]=a[i]=0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    「2020 新手必备 」极速入门 Retrofit + OkHttp 网络框架到实战,这一篇就够了!
    结合源码,重温 Android View 的事件处理知多少 ?
    Android 这 13 道 ContentProvider 面试题,你都会了吗?
    17 个必须掌握的 BroadcastReceiver 知识点「建议收藏」
    23 个重难点突破,带你吃透 Service 知识点「长达 1W+ 字」
    Activity 的 36 大难点,你会几个?「建议收藏」
    Python time模块
    vue项目的创建
    githunb和码云生成/添加SSH公钥
    weex打包apk步骤
  • 原文地址:https://www.cnblogs.com/SCL123/p/11873406.html
Copyright © 2011-2022 走看看