odd-even number
For a number,if the length of continuous odd digits is even and the length of continuous even digits is odd,we call it odd-even number.Now we want to know the amount of odd-even number between L,R(1<=L<=R<= 9*10^18).
Input First line a t,then t cases.every line contains two integers L and R. Output Print the output for each case on one line in the format as shown below. Sample Input
2 1 100 110 220Sample Output
Case #1: 29 Case #2: 36
通过这题了解了数位DP的正确打开方式。
1.一般不需要预处理出dp数组的值,直接记忆化就好了
2.先写好直接搜索的代码,然后在加上dp数组进行记忆化,这样比较不容易混乱
3.dfs函数里一般都需要传一个limit值,表示当前这一位是否需要限制。不限制的话可以从9枚举到0,并且可以记录或调用dp数组的值。如果限制只能从当前的值枚举到0。
对于这道题:
题意:问[l,r]区间内有多少个数满足,他的数位由连续偶数个奇数,连续奇数个偶数组成。
思路:dp[i][j][k][l]表示第i位,前一位是多少,是否存在前导零,当前是否已经满足要求。
具体解释见代码注释
code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 30;
int digit[maxn];
ll dp[maxn][10][2][2];
//当前第i位,前一位是pre,是否有前导零,当前是否已经满足条件,是否需要限制
ll dfs(int i,int pre,bool zero,bool yes,bool limit){
//如果整个数枚举完,满足条件返回1
if(i == -1){
return yes;
}
//能枚举到的最大的数
int max_digit = limit ? digit[i] : 9;
ll &dpnow = dp[i][pre][zero][yes];
//如果不需要限制并且dp不为-1,直接返回值,记忆化搜索
if(!limit && dpnow != -1)
return dpnow;
ll ans = 0;
//now==0时,zero为true,因为这是有前导零的情况,所以枚举的数字作为第一位
//长度相当于1是奇数所以必须只有枚举偶数的时候yes是true,当前有限制且now为
//最大数则仍然需要限制
if(zero){
for(int now = 0; now <= max_digit; now++){
ans += dfs(i-1,now,now == 0,!(now & 1),limit && now == max_digit);
}
}
//此时zero一定是false,那么我们枚举当前位的时候如果说当前位的奇偶性和前一位不同
//说明此时是前一部分相同奇偶性的那段连续数位已经结束,开始另一个奇偶性的连续数位
//那么这是我们枚举的第一位长度也就是为1,如果要使得当前情况满足就必须使当前位是偶数
//如果当前位是偶数那么它的前一位就必须是奇数
else if(yes){
for(int now = 0; now <= max_digit; now++){
ans += dfs(i-1,now,false,(pre & 1) && !(now & 1),limit && now == max_digit);
}
}
//如果当前不满足条件,为了满足条件,必须使得当前枚举位的奇偶性和前一位相同
//因为数位要么奇数要么偶数如果不满足情况只有两种可能,偶数位奇数个,奇数位偶数个
//那么加上一个和其同奇偶的就一定使得条件满足此时yes一定为true
else{
for(int now = (pre & 1); now <= max_digit; now += 2){
ans += dfs(i-1,now,false,true,limit && now == max_digit);
}
}//这一部也保证了相同奇偶性的数字必是连续的
//更新dp值,记忆化
if(!limit){
dpnow = ans;
}
return ans;
}
ll solve(ll n){
if(n == 0) return 1;
int len = 0;
while(n){
digit[len++] = n % 10;
n /= 10;
}
return dfs(len-1,0,true,true,true);
}
int main(){
memset(dp,-1,sizeof(dp));
int T;
scanf("%d",&T);
for(int cas = 1; cas <= T; cas++){
ll l,r;
scanf("%lld%lld",&l,&r);
ll ans = solve(r) - solve(l-1);
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}