数位dp进阶:[CQOI2016]手机号码
我自己居然写对了!!!!
这道题目我们要充分考虑所有情况。首先要求三位连续的相同,我们开一个bool变量,来记录是否已经满足了条件。有一个小细节:
if (len<=0) return tre;
这样如果搜完了,满足条件就返回1,否则为0.
另外,4和8不能同时出现在号码中,所以开两个bool数组,来记录。同时,记忆化的细节要注意,必须满足所有条件才能return。
还有,就是第一位一定不是0,直接在一开始枚举1-num[cnt]就好了,注意如果i!=num[cnt],limit要设为0;
code:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define int long long
using namespace std;
const int maxn=14;
int l,r,num[99],dp[maxn][maxn][maxn][maxn][2][2],cnt;
inline int dfs(int len,int firs,int sec,bool lmt,bool tre,bool eight,bool four)
{
int shit=9;
if (lmt) shit=num[len];
int ans=0;
if (four&&eight) return 0;
if (len<=0) return tre;
if (~dp[len][firs][sec][tre][eight][four]&&!lmt) return dp[len][firs][sec][tre][eight][four];
for (int i=0;i<=shit;++i)
{
ans+=dfs(len-1,sec,i,lmt&&i==shit,tre||( (i==sec)&&(i==firs)),(i==8)||eight,(i==4)||four);
}
dp[len][firs][sec][tre][eight][four]=ans;
return ans;
}
signed main()
{
cin>>l>>r;
while (r!=0)
{
num[++cnt]=r%10;
r/=10;
}
memset(dp,-1,sizeof(dp));
int fina1=0,fina2=0;
for (int i=1;i<=num[cnt];++i)
{
fina1+=dfs(cnt-1,0,i,i==num[cnt],0,i==8,i==4);
}
memset(dp,-1,sizeof(dp));
memset(num,0,sizeof(num));
cnt=0;
l--;
if (l>=1e10)
{
while (l!=0)
{
num[++cnt]=l%10;
l/=10;
}
for (int i=1;i<=num[cnt];++i)
{
fina2+=dfs(cnt-1,0,i,i==num[cnt],0,i==8,i==4);
}
}
cout<<fina1-fina2<<endl;
return 0;
}
收获:
数位dp有三个需要斟酌的点:1、初始状态。2、判断是否合法(扫完后如果合法return 1)。3、记忆化(注意一定要满足所有条件并且limit==0才可以转移)