zoukankan      html  css  js  c++  java
  • 【数位DP】【SCOI2009】windy数

    传送门

    Description

    (windy)定义了一种(windy)数。不含前导零且相邻两个数字之差至少(2)的正整数被称为(windy)数。(windy)想知道,

    (A)(B)之间,包括(A)(B),总共有多少个(windy)数?

    Input

    包含两个整数,(A,B)

    Output

    一个整数

    Sample Input

    25 50
    

    Sample Output

    20
    

    Hint

    (For~All:)

    (1~leq~A~leq~B~leq~2~ imes~10^9)

    Solution

    数位DP。

    数位DP的DP状态一般含有如下参数:

    1、从高到低当前填到了第几位。
    2、当前这一位的数是几。
    3、这一位是否等于一个上界x。一般而言,大于上界没有意义,所以可以用一个二进制表示等于或小于。
    4、从最高位到这一位是否全为0

    对于具体题目,需要根据要求增删状态。

    对于本题而言,可以设(f_{i,j,0/1,0/1})代表从高到低填到了第i位,当前这一位数字是j,否/是等于0,否/是全为0的方案数

    考虑([A,B])间的答案就等于小于(B)的答案减去小于(A-1)的答案。于是可以分别把(B)(A-1)作为上界x求得答案相减。

    预处理:处理出第一位所有的情况。具体的,设第一位是(s_1),则(f_{1,j,0,0}=1|j<s_1),(f_{1,0,0,1}=1),(f_{1,s_1,1,0}=1)

    转移方面,这里使用刷表法刷出下一维。具体的,枚举当前是什么状态,枚举下一位的数字是谁。

    直接累加小于上界且从小于上界的状态转移的答案,对于等于上界的状态,转移到等于上界的状态。注意区分小于和等于在第3维上的差异。

    显然每一位全是前导零的方案数是1。对于本位置全是前导0的方案,可以更新下一位填任意位置小于上界的状态。

    最后累加答案为(ans=(sum_{j<s_{len}}f_{len,j,0,0})+f_{len,s_{len},1,0})。其中len为上界x的位数。

    看着发晕的话可以参考代码

    Code

    #include<cstdio>
    #include<cstring> 
    #define rg register
    #define ci const int
    #define cl const long long int
    
    typedef long long int ll;
    
    namespace IO {
        char buf[90];
    }
    
    template<typename T>
    inline void qr(T &x) {
        char ch=getchar(),lst=' ';
        while(ch>'9'||ch<'0') lst=ch,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(lst=='-') x=-x;
    }
    
    template<typename T>
    inline void write(T x,const char aft,const bool pt) {
        if(x<0) x=-x,putchar('-');
        int top=0;
        do {
            IO::buf[++top]=x%10+'0';
            x/=10;
        } while(x);
        while(top) putchar(IO::buf[top--]);
        if(pt) putchar(aft);
    }
    
    template<typename T>
    inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
    template<typename T>
    inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
    template<typename T>
    inline T mabs(const T a) {if(a<0) return -a;return a;}
    
    template<typename T>
    inline void mswap(T &a,T &b) {
        T temp=a;a=b;b=temp;
    }
    
    const int maxs = 15;
    
    ll a,b;
    ll frog[maxs][maxs][5][5],st[maxs];
    
    ll dp(ll x);
    
    int main() {
    	qr(a);qr(b);
    	a=dp(a-1);memset(frog,0,sizeof frog);b=dp(b);
    	write(b-a,'
    ',true);
    	return 0;
    }
    
    ll dp(ll x) {
    	int len=0;
    	rg ll tx=x;
    	do {++len;} while(tx/=10);if(!x) len=0;						//确定x的位数 
    	for(rg int i=len;i;--i) st[i]=x%10,x/=10;					//st[i]即为s数组,存储x每一位的值 
    	for(rg int i=1;i<st[1];++i) frog[1][i][0][0]=1;
    	frog[1][st[1]][1][0]=frog[1][0][0][1]=1;							
    	/*
    		*初始化:
    		*第一位填小于s[1]的数,整个数小于x前1位且无前导0的方案数
    		*=第一位填s[1]的数,整个数等于x前1位的方案数
    		*=全部填0的方案数=1 
    	*/
    	for(rg int i=1;i<len;++i) {
    		rg int di=i+1;											//下一位置 
    		for(rg int j=0;j<10;++j) {
    			for(rg int k=0;k<10;++k) {
    				if(mabs(j-k) >= 2) {							//如果这一位合法 
    					frog[di][k][0][0]+=frog[i][j][0][0];		//这一位填k的方案数,从上一位的数小于x转移 
    					if(k < st[di]) frog[di][k][0][0]+=frog[i][j][1][0];
    																//从上一位等于x的数转移到这一位小于x的答案 
    					else if(k == st[di]) frog[di][k][1][0]+=frog[i][j][1][0];
    																//从上一位等于x的数转移到这一位等于x的答案 
    				}
    			}
    		}
    		frog[di][0][0][1]=1;									//下一位全是前导0的方案数为1 
    		for(rg int j=1;j<10;++j) frog[di][j][0][0]+=frog[i][0][0][1];
    																//由这一位全是前导0可以更新下一位的不取0的所有情况。 
    	}
    	ll _ans=0;
    	for(rg int i=0;i<10;++i) _ans+=frog[len][i][0][0];
    	_ans+=frog[len][st[len]][1][0];
    	return _ans;
    }
    

    Summary

    数位dp状态的确定:

    1、从高到低当前填到了第几位。
    2、当前这一位的数是几。
    3、这一位是否等于一个上界x。一般而言,大于上界没有意义,所以可以用一个二进制表示等于或小于。
    4、从最高位到这一位是否全为0

    转移细节比较多,需要留心

  • 相关阅读:
    linux中的umask命令
    The meaning of the number displayed on the man page in Linux
    Runlevel in Linux
    C语言指针与指向指针的指针
    JS函数、变量作用域
    JS对象、数据类型区别、函数
    JavaScript基础
    DOM
    HTML色码表
    GIT
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9764386.html
Copyright © 2011-2022 走看看