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; }