P4124 [CQOI2016]手机号码(数位DP)

题目描述

人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。

工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。

手机号码一定是 1111 位数,前不含前导的 00。工具接收两个数 LL 和 RR,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。LL 和 RR 也是 1111 位的手机号码。

输入格式

输入文件内容只有一行,为空格分隔的 22 个正整数 L,RL,R

输出格式

输出文件内容只有一行,为 11 个整数,表示满足条件的手机号数量。

#include<bits/stdc++.h>
using namespace std;
const int maxn=70;
const int mod=1e9+7;
typedef long long ll;
ll L,R,f[maxn][15][15][2][2][2];
//f(i,j,k,l)表示当前到第i位,上一位数字,上上位数字,是否有8和4,是否有3位连续相同的数字的状态 
int a[maxn],len;
int tot=0;
ll dfs (int pos,int pre1,int pre2,int st,int limit,bool x,bool y,bool z) {
	if (pos<=0) {
		if (z==1&&!(x&&y)) return 1;
		return 0;
	}
	if (!limit&&f[pos][pre1][pre2][x][y][z]!=-1) {
		return f[pos][pre1][pre2][x][y][z];
	}
	ll ans=0;
	ll k=limit?a[pos]:9;
	for (int i=0;i<=k;i++) {
		if (st&&i==0) {
			ans+=dfs(pos-1,pre1,pre2,1,limit&&i==k,x,y,z);
		}
		else {
			if (i==pre1&&i==pre2)
				ans+=dfs(pos-1,i,pre1,0,limit&&i==k,(x||(i==8)),(y||(i==4)),1);
			else
				ans+=dfs(pos-1,i,pre1,0,limit&&i==k,(x||(i==8)),(y||(i==4)),z);
		}
	}
	if (!limit&&!st) {
		f[pos][pre1][pre2][x][y][z]=ans;
	}
	return ans;
}
ll solve (ll x){
	len=0;
	while (x) a[++len]=x%10,x/=10;
	memset(f,-1,sizeof(f));
	return dfs(len,10,10,1,1,0,0,0); 
}
int main () {
	cin>>L>>R;
	cout<<solve(R)-solve(L-1);
}
posted @ 2021-03-28 22:32  zlc0405  阅读(76)  评论(0编辑  收藏  举报