现在的位置: 首页 > 算法 > 正文

[bzoj 1026]windy数[数位DP]

2019年04月05日 算法 ⁄ 共 1230字 ⁄ 字号 评论关闭

windy数的定义:

相邻两数位之间的差不小于2.
这个定义表明windy数的一部分一也是windy数.

较长的windy数与较短的windy数之间的关系:

当较短的数是windy数时,只要考虑加的那一位和最高位之间的差是否大于1.
最高位.

于是可设计状态:

dp[i][j]----有i位的数中,最高位为j的windy数的个数.

状态转移方程:

dp[i][j] += dp[i-1][k]( k = 0..9 && |j-k| < 2)

预处理是考虑首位为0的情况的,因为用它的时候它常常不是首位.计算的时候避免即可.

计算结果:

这个时候必须分开算.不足len位的情况要累加.因为此时不考虑先导0.
如果像Bomb那道题一样直接并入最高位的计算的话就考虑了先导0.

len位数的计算就仿照Bomb了.

仍然注意:

从高位到低位,讨论的数逐渐增大逼近上界,当某一位固定为子串最高位时(即与原数的该位相同时)

若恰好违背windy数,那么计算终止.因为此位已经是最大的了,而以此位为最高位的所有数都非法,

也就是说此后直到上界,都不可再增加,因此计数终止.

//808 kb	0 ms
#include <cstdio>
using namespace std;
const int MAXN = 12;
typedef long long ll;
ll dp[MAXN][10];
int bit[MAXN];
int len;
int abs(int x)
{
    return (x + (x>>31))^(x>>31);
}
void InitDP()
{
    for(int i=0;i<10;i++)        dp[1][i] = 1;
    for(int i=2;i<MAXN;i++)
        for(int j=0;j<10;j++)
            for(int k=0;k<10;k++)
                if(abs(j-k)>1) dp[i][j] += dp[i-1][k];
}
void pre(int x, int &len)
{
    int i;
    for(i=1; x; i++)
    {
        bit[i] = x % 10;
        x /= 10;
    }
    bit[i] = 0;
    len = i-1;
}
ll cal(int x)
{
    ll ret = 0;
    for(int i=1;i<len;i++)
        for(int j=1;j<10;j++)
            ret += dp[i][j];
    for(int i=1;i<bit[len];i++)
        ret += dp[len][i];
    for(int i=len-1; i; i--){
        for(int j=0;j<bit[i];j++)
            if(abs(bit[i+1]-j)>1)   ret += dp[i][j];
        if(abs(bit[i+1]-bit[i])<2)  break;
    }
    return ret;
}
int main()
{
    int a,b;
    InitDP();
    while(scanf("%d %d",&a,&b)==2)
    {
        ll ansa,ansb;
        pre(a,len);
        ansa = cal(a);
        pre(++b,len);
        ansb = cal(b);
        printf("%lld\n",ansb-ansa);
    }
}

写%I64d\n居然是PE啊= =

抱歉!评论已关闭.