[WOJ2755]#2755「CQOI2016」手机号码

该博客介绍了一道关于手机号码的题目,要求号码中至少有3个相邻的相同数字且不能同时出现8和4。博客内容涉及如何利用数位动态规划解决此问题,并提到了在实现过程中需要注意的细节,如前导0和边界情况。
摘要由CSDN通过智能技术生成

#2755「CQOI2016」手机号码

题面
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 3个相邻的相同数字;号码中不能同时出现 8 和 4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 11 位数,前不含前导的 0。工具接收两个数 L 和 R,自动统计出[L,R] 区间内所有满足条件的号码数量。L 和 R 也是 11 位的手机号码。

输入
输入文件内容只有一行,为空格分隔的两个正整数 L,R
输出
输出文件内容只有一行,为一个整数,表示满足条件的手机号数量。
样例输入
12121284000 12121285550
样例输出
5
提示
样例解释
满足条件的号码有: 12121285000 、 12121285111 、 12121285222 、 12121285333 、 12121285550 。 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。 1212128500012121285111121212852221212128533312121285550
对于 30% 的数据, R − L ≤ 1 0 7 R−L≤10^7 RL107
对于 100% 的数据, 1 0 10 ≤ L ≤ R &lt; 1 0 11 10^{10}≤L≤R&lt;10^{11} 1010LR<1011

SOL
很明显这是一道数位DP题
不过这道题的状态定义真的很绕,我定义了六维,分别表示位数,上个数,上上个数,有没有连续三个数,有没有4,有没有8,然后再做记忆化搜索。
值得注意的是,这道题只能出现11位的数,所以考虑前导0,但是只要你第一位从1开始 D P DP DP就可以了,不过要注意1e10作为左端点的情况,非常坑!!!WA了三个点!!!

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
inline int rd(){
	int register data=0;static char ch=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
	return data;
}
int f[15][10][10][2][2][2],h[15],a,b;
inline int dp(int pos,int pre,int pre_pre,int tri,int _4,int _8,bool flag){//tri:有连续三个相同的数 _8:有8 _4:有4 
	if(_4&&_8)return 0;
	if(!pos)return tri;
	if(!flag&&f[pos][pre][pre_pre][tri][_4][_8]!=-1)return f[pos][pre][pre_pre][tri][_4][_8];
	int register maxl=flag?h[pos]:9,ans=0;
	for(int register i=0;i<=maxl;i++){
		bool register now_flag=(flag&&i==maxl);
		int register now_tri=(tri||(pre_pre==pre&&pre==i)),now__4=(_4||i==4),now__8=(_8||i==8);
		ans+=dp(pos-1,i,pre,now_tri,now__4,now__8,now_flag);
	}
	if(!flag)f[pos][pre][pre_pre][tri][_4][_8]=ans;
	return ans;
}
inline int solve(int x){
	int register len=0,ans=0;
	while(x){
		h[++len]=x%10;
		x/=10;
	}
	for(int register i=1;i<=h[len];i++){
		memset(f,-1,sizeof(f));
		ans+=dp(len-1,i,0,0,i==4,i==8,i==h[len]);
	}
	return ans;
}
signed main(){
	a=rd();b=rd();
	if(a!=(int)1e10)printf("%lld",solve(b)-solve(a-1));
	else printf("%lld",solve(b));
	return 0;	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值