求n到m之间的吉利手机号,号码中要出现至少 3 个相邻的相同数字;号码中不能同时出现 8和 4。
这个题需要考虑两个点,一个是前导0,因为有前导0的必然不是手机号,一个是长度必须是11,否则也不是手机号
至于前导0,可以额外开一个维度,记录当前是否有前导0,但是会时内存x2,本题还不会爆,但是别的题就不一定了:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
#define int LL
int num[32], cnt, dp[32][15][15][2][2][2][2][15];
int dfs(int pos, int pre1, int pre2,int have8,int have4,int state,int limit,int pre0,int len) {
//pos为dp的第一维 stat为第二维 limit代表当前枚举数字是否达到上限
if (pos == -1){
if (len != 11) return 0;
if (state && !(have8 && have4)) return 1;
else return 0;
}
if (!limit &&(pre1!=-1)&&(pre2!=-1)&& dp[pos][pre1][pre2][have8][state][have4][pre0][len] != -1) return dp[pos][pre1][pre2][have8][state][have4][pre0][len] ;
//如果没有达到上限,即可以随便枚举,且枚举的情况已经记录过了,就直接返回
int up;
if (limit) //如果前面都达到上限了,那么这次也必须只能枚举到上限
up = num[pos];
else
up = 9;
int res = 0;
for (int i = 0; i <= up; i++) {
if (have8 && (i == 4)) continue;
if (have4 && (i == 8)) continue;
res += dfs(pos - 1, pre2,i,have8|(i==8),have4|(i==4), state|(pre1==pre2&&pre2==i),limit && i == num[pos],pre0&&(i==0),pre0&&(i==0)?0:len+1);
}
if (!limit&&(pre1!=-1)&&(pre2!=-1)) dp[pos][pre1][pre2][have8][state][have4][pre0][len] = res;
return res;
}
int solve(LL n) {
cnt = 0;
while (n) {
num[cnt++] = n % 10;
n /= 10;
}
return dfs(cnt - 1,-1,-1, 0,0,0, 1,1,0); //第一个数必须由上限来限制,所以limit=1
}
signed main() {
LL n, m;
scanf("%lld%lld", &n, &m);
memset(dp, -1, sizeof dp);
printf("%lld
", solve(m) - solve(n - 1));
return 0;
}
还有一种写法是直接在枚举当前位置上的数的时候,可以判断是否是第一位,如果是第一位,那么就只能从1开始:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
#define int LL
int num[32], cnt, dp[32][15][15][2][2][2];
int dfs(int pos, int pre1, int pre2,int have8,int have4,int state,int limit) {
//pos为dp的第一维 stat为第二维 limit代表当前枚举数字是否达到上限
if (pos == -1){
if (state && !(have8 && have4)) return 1;
else return 0;
}
if (!limit &&(pre1!=-1)&&(pre2!=-1)&& dp[pos][pre1][pre2][have8][state][have4] != -1) return dp[pos][pre1][pre2][have8][state][have4] ;
//如果没有达到上限,即可以随便枚举,且枚举的情况已经记录过了,就直接返回
int up;
if (limit) //如果前面都达到上限了,那么这次也必须只能枚举到上限
up = num[pos];
else
up = 9;
int res = 0;
for (int i = (pos==10)?1:0; i <= up; i++) {
if (have8 && (i == 4)) continue;
if (have4 && (i == 8)) continue;
res += dfs(pos - 1, pre2,i,have8|(i==8),have4|(i==4), state|(pre1==pre2&&pre2==i),limit && i == num[pos]);
}
if (!limit&&(pre1!=-1)&&(pre2!=-1)) dp[pos][pre1][pre2][have8][state][have4] = res;
return res;
}
int solve(LL n) {
cnt = 0;
while (n) {
num[cnt++] = n % 10;
n /= 10;
}
if (cnt != 11) return 0;
return dfs(cnt - 1,-1,-1, 0,0,0, 1); //第一个数必须由上限来限制,所以limit=1
}
signed main() {
LL n, m;
scanf("%lld%lld", &n, &m);
memset(dp, -1, sizeof dp);
printf("%lld
", solve(m) - solve(n - 1));
return 0;
}