Odometer
Farmer John’s cows are on a road trip! The odometer on their car displays an integer mileage value, starting at X (100 <= X <= 10^18)miles at the beginning of their trip and ending at Y (X <= Y <= 10^18)miles at the end of their trip. Whenever the odometer displays an ‘interesting’ number (including at the start and end of the trip) the cows will moo. A number is ‘interesting’ if when you look at all its digits except for leading zeros, at least half of these should be the
same. For example, the numbers 3223 and 110 are interesting, while the numbers 97791 and 123 are not.
Help FJ count how many times the cows will moo during the trip.PROBLEM NAME: odometer
INPUT FORMAT:
Line 1: The first line will contain two integers, X and Y, separated by a space.
SAMPLE INPUT (file odometer.in):
110 133
INPUT DETAILS:
The trip starts with the odometer at 110 and ends at 133.
OUTPUT FORMAT:
Line 1: A single integer containing how many times the cows will mooduring the trip.
SAMPLE OUTPUT (file odometer.out):
14
OUTPUT DETAILS:
The cows moo when the odometer reads 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 121, 122, 131, and 133.
Task:如果一个数有至少一半的数字相同,那么FJ的牛就会moo~。给定100<=A,B<=1018,求区间[A,B]中的数字有多少个会使FJ的牛moo~。
Solution:
一道很不错的数位dp题。(其实是我不会做)
我们可以将问题转化为calc(B)−calc(A−1)。
定义dp[i][und][k][is0]:i为当前要填的位数,
转移的方式即是枚举当前位的数字的取值,转移到下一位。
于是calc(x)的求法就是显而易见的了:
- 枚举超过一半次数的那个数字,将所有的可能加起来
- 我们会发现有一种情况被算了两次:即时两种数字各占一半:如2626,9449等,我们需要减掉这些情况。
#include<stdio.h>
#include<string.h>
#include<iostream>
#define M 50
#define ll long long
using namespace std;
int sz,A[M];
ll dp[M][2][M+5][2];
void check(ll &a,ll b){
if(a==-1)a=0;
a+=b;
}
ll Dp(int t1,int t2){
memset(dp,-1,sizeof(dp));
dp[1][true][25][true]=1;//25为基数
for(int i=1;i<=sz;i++)
for(int und=0;und<2;und++)
for(int k=0;k<=M;k++)
for(int is0=0;is0<2;is0++){
if(dp[i][und][k][is0]==-1)continue;
for(int nxt=0;nxt<=9;nxt++){
if(t2!=-1&&nxt!=t1&&nxt!=t2&&(!(nxt==0&&is0)))continue;//当前面是前导0时,当前填0可以忽略不计
if(und&&nxt>A[i])break;//超过原数
int nis0=is0&(nxt==0);
int nk=k;
if(!nis0){
if(t2==-1){
if(t1==nxt)nk--;
else nk++;
}else{
if(t1==nxt)nk--;
else if(nxt==t2)nk++;
}
}
int nund=und&(nxt>=A[i]);
check(dp[i+1][nund][nk][nis0],dp[i][und][k][is0]);
}
}
ll res=0;
if(t2==-1){
for(int i=0;i<2;i++)
for(int k=0;k<=25;k++){
ll rm=dp[sz+1][i][k][false];
if(rm!=-1)res+=rm;
}
}else{
for(int i=0;i<2;i++){
ll rm=dp[sz+1][i][25][false];//两种数字的个数之差=0
if(rm!=-1)res+=rm;
}
}
return res;
}
ll calc(ll x){
sz=0;
ll b=1,res=0;
if(x>=1e18)b=1e18;//无奈的防溢出...
else{
while(x/b)b*=10;
b/=10;
}
while(b){
A[++sz]=x/b%10;
b/=10;
}
for(int i=0;i<=9;i++)//计算
res+=Dp(i,-1);
for(int i=0;i<=9;i++)
for(int j=i+1;j<=9;j++)//特殊情况
res-=Dp(i,j);
return res;
}
int main(){
ll a,b;
cin>>a>>b;
cout<<calc(b)-calc(a-1)<<endl;
return 0;
}