数位DP的一道所谓的水题!
但是呢,必须得胆子大!
题意:求区间【L,R】中有多少个满足条件的十进制数
条件是:任意连续K个数值都不相同,K=2,3,4,5
dp最难的是设计状态和状态转移啊!
K很小,所以可以每一个有意义的数位都来一维来表示呗
dp[pos][p1][p2][p3][p4]
pos表示当前位,p4表示前一位。这里要考虑前导0的情况,p4=10的时候表示前一位为0
然后呢,最特殊的情况是:全是0的情况
转移呢,枚举即可
判断条件要根据K来说咯,如果K=2,那么当前枚举的这一位不等于p4
K=3,4,5是同理的
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define LL __int64
const int maxn=25;
int K,digit[maxn];
LL dp[maxn][11][11][11][11];
bool check(int p1,int p2,int p3,int p4,int u){
if (K==2) return u!=p4;
else if (K==3) return u!=p4&&u!=p3;
else if (K==4) return u!=p4&&u!=p3&&u!=p2;
else return u!=p4&&u!=p3&&u!=p2&&u!=p1;
}
LL dfs(int pos,int p1,int p2,int p3,int p4,bool flag){
if (pos==0) return p4!=10;
if (flag&&dp[pos][p1][p2][p3][p4]!=-1) return dp[pos][p1][p2][p3][p4];
LL res=0;
int maxnum=flag?9:digit[pos];
for(int i=0;i<=maxnum;i++){
if (p4==10&&i==0)
res+=dfs(pos-1,10,10,10,10,flag||i<maxnum);
else if (check(p1,p2,p3,p4,i))
res+=dfs(pos-1,p2,p3,p4,i,flag||i<maxnum);
}
if (flag) dp[pos][p1][p2][p3][p4]=res;
return res;
}
LL calc(LL x){
int len=0;
while(x){
digit[++len]=x%10;
x/=10;
}
return dfs(len,10,10,10,10,0);
}
int main(){
//freopen("input.txt","r",stdin);
LL L,R;
while(scanf("%I64d%I64d%d",&L,&R,&K)!=EOF){
memset(dp,-1,sizeof(dp));
printf("%I64d\n",calc(R)-calc(L-1));
}
return 0;
}
注意:
在数位dp分隔数位的时候
一定是digit【++len】而不是digit【len++】
一方面是因为最后的初始边界是len==0,就算 改成len==-1也是不行的
因为:dp的初始化会导致有问题