SOJ 4265: story of 206

其实我出这道题的心态还是挺健康的,

就是学习了一种很简洁的数位DP方法,

想推荐给大家。

结果这道题比赛的时候只有三个人开,

且最后只有一个过,

哭了。。。

赛后给开了这道题的三个人都发了标程。

然后就没有然后了。。

sigh。。。。

其实DP还是多有趣的。。。

小盆友们要多学习一下啊~~~


题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=4265


题意:给定一个闭区间,问该区间中满足含有“206”这个子串并且任意相邻两位数字均不相同的数有多少个。


算法:

枚举数的位数记忆化搜索。

数组需记录四维变量。

fst表示当前位置是否是首位。

len表示当前处理的是串的第len位。

s表示当前串含有的“206”前缀(例如可以用0表示无前缀,1表示“2”,2表示“20”,3表示“206”等等)。

pre表示当前位的前一位是哪个数字。

然后根据记录的信息枚举各位置的数字状态转移即可。


代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

long long d[2][20][4][10];
long long dig[20];

int ns(int s,int i) {
    if(s==3||(s==2&&i==6)) {
        return 3;
    }
    if(s==1&&i==0) {
        return 2;
    }
    if(i==2) {
        return 1;
    }
    return 0;
}

long long dp(bool less,bool fst,int len,int s,int pre) {
    if(len==-1) {
            return less&&s==3;
    }
    if(less&&d[fst][len][s][pre]!=-1) {
        return d[fst][len][s][pre];
    }
    long long ret=0LL;
    for(int i=0; i<10; i++) {
        if(!less&&i>dig[len]) {
            continue;
        }
        if(i==pre) {
            continue;
        }
        if(fst&&!i) {
            continue;
        }
        ret+=dp(less||i<dig[len],false,len-1,ns(s,i),i);
    }
    if(less) {
        d[fst][len][s][pre]=ret;
    }
    return ret;
}

long long solve(long long x) {
    int len=0;
    while(x) {
        dig[len++]=x%10;
        x/=10;
    }
    long long ret=0LL;
    for(int i=0; i<len; i++) {
        ret+=dp(i<len-1,true,i,0,-1);
    }
    return ret;
}

int main() {
    int cas;
    scanf("%d",&cas);
    memset(d,-1,sizeof(d));
    while(cas--) {
        long long a,b;
        scanf("%lld%lld",&a,&b);
        printf("%lld\n",solve(b+1)-solve(a));
    }
    return 0;
}


你可能感兴趣的:(SOJ 4265: story of 206)