您的位置:首页 > 其它

SOJ 4265: story of 206

2013-04-16 12:49 239 查看
其实我出这道题的心态还是挺健康的,

就是学习了一种很简洁的数位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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: