zoukankan      html  css  js  c++  java
  • $P2657 [SCOI2009] windy$数

    属于数位$DP$入门级别的题目,但我做这类题不多,还是要总结一下这道经典题目 #$Description$ [题面](https://www.luogu.org/problem/P2657) 给定$a,b$,求$[a,b]$区间有多少个数满足:任意两个相邻数位之间的差的绝对值$>=2$ $a,b<=1e12$ #$Solution$ 数位$DP$的基本思想是一个一个数确定,逼近到边界 数位$DP$一般设计状态为$dp[i][s]$表示当前考虑到第$i$位(从最低位编号),当前位置或附近位置状态为$s$的方案数。 有时候需要预处理,有时候直接数位$dp$即可

    对于这个题,比较显然的是设计状态(dp[i][j])表示当前考虑到第(i)位((1--(i-1))都已经考虑),第(i)位为(j)的方案数
    状态转移比较显然,注意也要处理(0)的情况

    void pre()
    {
    	pow[0]=1;
    	for(re int i=1;i<=13;++i) pow[i]=pow[i-1]*10;//pow[1]=10,i表示1o^i;
    	for(re int i=0;i<=9;++i) dp[1][i]=1;//单独处理一位的情况 
    	for(re int i=2;i<=12;++i)//注意从2开始 
    	 for(re int j=0;j<=9;++j)
    	  for(re int k=0;k<=9;++k)
    	  	if(abs(j-k)>=2) dp[i][j]+=dp[i-1][k];//把0的情况也处理 
    }
    

    统计答案时就照着数位(DP)的套路统计,不过这道题要注意前导零,比如例子:(65536)
    MK6MvV.png
    如此统计还有几个问题:
    (1.)不能直接统计(00000-59999),因为像(01xxx)这样的答案会被判断为不合法,但是答案要求不包含前导零,所以这种情况是合法的。所以我们要枚举答案时(1、2、3、4)位数的情况,来消除前导(0)的影响
    (2.)注意到每次逼近都是枚举到当前位置的数(-1),因为这样后面的位置可以考虑所有情况,所以最后会枚举到(65535)而忽略(65536),把边界设到(x+1)(65537)就行了。
    (3.)注意(65xxx)的答案实际上已经不合法了,我们枚举第二高位的时候会排除这种情况,但到了下一位直接默认这一位是(5)了,因此就不合法了。所以每次跳到下一位之前要判断这一位和前一位是否合法,不合法直接退出,(return),因为后面美剧的都是(65xxx),都不合法不用考虑了。
    更多细节见代码

    (Code)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define re register
    #define ll long long
    using namespace std;
    inline ll read()
    {
    	ll x=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    ll a,b;
    ll dp[20][20],pow[20],cnt;
    void pre()
    {
    	pow[0]=1;
    	for(re int i=1;i<=13;++i) pow[i]=pow[i-1]*10;//pow[1]=10,i表示1o^i;
    	for(re int i=0;i<=9;++i) dp[1][i]=1;//单独处理一位的情况 
    	for(re int i=2;i<=12;++i)//注意从2开始 
    	 for(re int j=0;j<=9;++j)
    	  for(re int k=0;k<=9;++k)
    	  	if(abs(j-k)>=2) dp[i][j]+=dp[i-1][k];//把0的情况也处理 
    }
    ll ask(ll x)
    {
    	ll tmp=x,ans=0,last;
    	cnt=0;
        while(tmp) {tmp/=10,cnt++;}
        int now=x/pow[cnt-1];//now表示当前考虑的最高位 
        for(re int i=cnt-1;i>=1;--i) 
         for(re int j=1;j<=9;++j)
          ans+=dp[i][j];//因为不包含前导0,所以01这种答案是合法的,枚举每个位置作为起点(1-(cnt-1))位数的开头
    	  //枚举的值是1-9,因为它是开头不含前导零,最后不用考虑0的情况,因为数据保证a>=1,考虑不考虑都会被前缀和相减消除 
        for(re int i=1;i<now;++i) ans+=dp[cnt][i];//第cnt位要单独处理 
        last=now;
        x%=pow[cnt-1];//last表示上一位,用于枚举下一位是判断 
        for(re int i=cnt-1;i>=1;--i)
        {
        	now=x/pow[i-1];//提取最高位 
        	for(re int j=0;j<now;++j)
        	if(abs(last-j)>=2) ans+=dp[i][j];
    		if(abs(now-last)<2) break;//!!!重要:假如这两个值不符合了,后面不用考虑了。
    		//比如10765,后面考虑的都是10xxx,一定都不符合了 
        	last=now;//更新一下上一位 
        	x%=pow[i-1];//注意取模保证每次取到当前的最高位 
        	
    	}
    	//统计的是开区间,边界数不会被统计
    	return ans; 
    	 
    }
    int main()
    { 
        a=read(),b=read();
        pre();
        ll tmp1=ask(b+1);
        ll tmp2=ask(a);
        printf("%lld
    ",tmp1-tmp2);//统计的是开区间,边界数不会被统计
    	return 0;
    }
    
    
  • 相关阅读:
    下载地址jquery upload file demo (C#)
    特征卷积基于3D卷积神经网络的人体行为理解(论文笔记)
    应用目录S5PV210的BL1应用
    手机音效手机测试游戏类
    metadata查询Querying Metadata
    arraynodeSorting
    functionclass[LeetCode]Path Sum II
    functionclass[LeetCode]Permutation Sequence
    exceptionfunction[LeetCode]Permutations
    exceptionfunction[LeetCode]Permutations II
  • 原文地址:https://www.cnblogs.com/Liuz8848/p/11832044.html
Copyright © 2011-2022 走看看