传送门: http://acm.hdu.edu.cn/showproblem.php?pid=5898
题目大意:
定义odd-even-number是一个数,其中连续奇数位的长度是偶数,连续偶数位的长度是奇数。
问[L,R]里有几个odd-even number?
题目分析:
其实呢我知道数位DP是干什么用的,但是没见过相关的题,所以即使一眼就看出用数位dp做,但却看了一天题解才弄懂这道题。
其实一开始我理解错题意了,以为是只要存在连续奇数位长度为偶数、连续偶数位长度为奇数就可以,但跑了一下别人的ac代码,发现并不是。
因为11222是odd-even-number,112221就不是,但1122233就是。
所以其实题意应该是,所有连续奇数位的长度都是偶数(包括0),所有连续位偶数的长度都是奇数。
这里用dp[i][j][k]表示,pos位,前一位是j,j那位前面有k个连奇或连偶的数的odd-even-number数。因为每次累加的时候都必须是满足pos往前k位满足连奇或连偶才加进去的,而pos+k位以上的又已经确保符合定义,所以是与当前子问题无关的。因此满足DP性质,go!
这里有两个要注意的,就是在搜索解的时候,要注意前导0,即假如要找1~12345范围内的解,我们是要从00001 搜索到 12345。而前导0对奇偶性是不影响的(即不算入连续偶数),第二个要注意的是搜索到第pos位的时候,该位的取值范围未必是0-9,是否有限制受高位影响(如果高位有限制,且当前数和上限的对应位相等)。
那么关键的问题来了,你DP总要有个转移方程啊,转移方程是什么呢?
我们记up代表第i位上的取值上限(可能是9,也可能是输入的那个上限r的对应位),那么有:
第三个下标取1还是k+1根据j和k的奇偶性判断。如果j奇k偶,或者j偶k奇,那么如果s跟j同奇偶性,就取k+1,否则取1(因为这时候前面的k位已经确保是解了,奇偶性变了自然从1再开始算),如果j、k奇偶性相同,那么只有s跟j同奇偶性才是解,而且取k+1,(例如前面是112222,如果再放一个1往下搜,就是不符合定义的)
然后编码的时候记住从高往低,深度优先搜索,因为位数是从pos-1到0的,所以从pos-1开始搜,搜到-1的时候说明前面都搜完了,只要看最后一轮连续数是否符合定义就好。
这题看了大半天,好。。。菜。。。啊。。。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll l,r;
ll dp[20][20][20];
int bit[20];
ll dfs(int pos,int j,int k,bool lead,bool limit) {
//从pos位开始,上一位是j,连续的奇数或者偶数有k,有没有前导0,第pos位是否有上界(即如果求1~8000,最高位只能取到8)
//返回这一条件下,符合答案的数
if(pos==-1)
return (j+k)%2;
if(!limit && !lead && dp[pos][j][k]!=-1)
return dp[pos][j][k];
int up = limit?bit[pos]:9; //范围到up
ll ans=0;
if(lead) {
for(int i=0;i<=up;i++) {
ans+=dfs(pos-1,i,1,i==0,limit && i==bit[pos]);
}
}
else if((j+k)%2==1) {
for(int i=0;i<=up;i++) {
if((i+j)%2==0)
ans+=dfs(pos-1,i,k+1,false,limit && i==bit[pos]);
else
ans+=dfs(pos-1,i,1,false,limit && i==bit[pos]);
}
}
else {
for(int i=0;i<=up;i++) {
if((i+j)%2==0)
ans+=dfs(pos-1,i,k+1,false,limit && i==bit[pos]);
}
}
if(!limit && !lead)
dp[pos][j][k] = ans;
return ans;
}
ll solve(ll x) { //计算[1,x]内有少符合条件的数
int pos=0;
bit[0]=0;
while(x) {//把数字x按位拆开放进数组里面,从最高位在pos-1,最低位是0
bit[pos++]=x%10;
x/=10;
}
// for(int i=0;i<pos;i++)
// printf("bit[%d] = %d\n", i,bit[i]);
return dfs(pos-1,0,1,true,true);
}
int main() {
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
for(int i=1;i<=t;i++) {
scanf("%I64d %I64d",&l,&r);
printf("Case #%d: %I64d\n", i,solve(r)-solve(l-1));
}
}