HDU-5898 数位DP [2016青岛网络赛]

还是数位DP,还是没做出来,模型是理解得可以了,编码的时候姿势不好,还是没办法通过的。
要学多点姿势,还是要多做题目。

找出[1,N]当中连续奇数位长度为偶数、连续偶数位为奇数的数字。
一般的姿势肯定想到以末尾段数位的奇偶性和其长度的奇偶性作为状态做记忆化搜索,本想着统一一下状态分类,降成一维状态,结果是自作聪明,在处理前导零的时候更加麻烦。
好的姿势可以避免重复计算非法的前导零,用一个状态位标记当前位前面合法连续串的长度,这个长度为0的时候说明要么前面非法了,要么前面是前导零,都特殊处理就行了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

long long dp[25][10][25];
int bit[25];
long long dfs(int pos, int istop, int p, int x){
    if (pos==-1){ 
        //边界条件,判断当前考虑的连续串的数位奇偶性和长度奇偶性
        //这里因为题目不会问到0,所以0当成非法串
        if (x && x%2!=p%2) return 1;
        else return 0;
    }
    if (!istop && dp[pos][p][x]!=-1) return dp[pos][p][x];

    int lastbit = istop ? bit[pos] : 9;
    long long ans = 0;
    for (int i=0;i<=lastbit;i++){
        if (x==0){ 
            //如果考虑的串长度是0,说明前面都是前导零,特殊处理一下
            if (i==0) ans += dfs(pos-1, istop&&i==lastbit, i, 0); //长度还是0
            else ans += dfs(pos-1, istop&&i==lastbit, i, 1); //遇见非零数位,长度置1
        }
        else { 
            //长度不为0的时候
            if (p%2 == i%2) ans += dfs(pos-1, istop&&i==lastbit, i, x+1); 
            //如果数位奇偶性不变,长度加1
            else if (x%2 != p%2) ans += dfs(pos-1, istop&&i==lastbit, i, 1); 
            //数位奇偶性发生变化的时候,记得判断结束的串的合法性
        }
    }

    if (!istop) dp[pos][p][x] = ans;
    return ans;
}

long long calc(long long n){
    if (n==0) return 0;
    int cnt = 0;
    while(n){
        bit[cnt++] = n%10;
        n /= 10;
    }
    memset(dp,-1,sizeof(dp));
    return dfs(cnt-1, 1, 0, 0);
}

int main()
{
    int t,kase=0;
    scanf("%d",&t);
    while(t--){
        long long l,r;
        scanf("%I64d%I64d",&l,&r);
        printf("Case #%d: %I64d\n",++kase, calc(r)-calc(l-1));
    }
    return 0;
}

你可能感兴趣的:(HDU-5898 数位DP [2016青岛网络赛])