题目:K-wolf Number
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5787
题意:给出L,R,K,求L到R之间有多少数满足:(10进制下)任意两个相等的数字至少相差K位。(1<=L<=R<=1e18,2<=k<=5)。
思路:
dp[i][j]:表示i 位且前面k位数为j 的情况有多少种,为了细节处理方便,dp数组形式改为:dp[pos][p1][p2][p3][p4]表示pos位,p1、p2、p3、p4是前面四位数字。初始化为10、10、10、10表示前导0。
采取记忆化搜索的姿势,dfs(pos,p1,p2,p3,p4,flag),flag=1表示这一位数有限制(不是题目要求中的限制,而是比如1234,那么第一位确定为1时,第二位最高只能2的限制。)具体在代码解释。
有点坑的是我尝试用dp[pos][pre]形式解的时候,第二维不能是1万,应该要10万,不然pre%1000*10+i,原本的1023会变成023i,会被误认为23i,就少判断了一个是否等于0。
AC代码:
1 #include<stdio.h>
2 #include<string.h>
3 typedef long long LL;
4 LL dp[20][11][11][11][11];
5 int bt[20],bo;
6 LL L,R;
7 int K;
8 bool check(int p1,int p2,int p3,int p4,int now) //根据K进行判断i是否可以和p1、p2、p3、p4共存
9 {
10 if(K==2) return now!=p4;
11 else if(K==3) return now!=p3 && now!=p4;
12 else if(K==4) return now!=p2 && now!=p3 && now!=p4;
13 else return now!=p1 && now!=p2 && now!=p3 && now!=p4;
14 }
15 LL dfs(int pos,int p1,int p2,int p3,int p4,bool flag)
16 {
17 if(pos==-1) return p4!=10; //结束判断,如果p4还等于10,那说明全是0,在这里,我规定0不满足条件。
18 if(!flag && dp[pos][p1][p2][p3][p4]!=-1) return dp[pos][p1][p2][p3][p4]; //如果以前存过值,直接取。
19 // 存取值都在flag=0的情况下进行,也就是接下来pos位无限制(只有题目限制)的条件下进行。
20 int limit= flag?bt[pos]:9; //limit表示这一位最高可以取到多少,没有限制就是9,有限制就是原数的这一位数字
21 LL ret=0;
22 for(int i=0;i<=limit;i++)
23 {
24 //如果p4还等于10表示前面全是前导0,i又取0,那么pos位还是前导0。
25 if(i==0 && p4==10) ret+=dfs(pos-1,p1,p2,p3,10,flag && i==limit);
26 else if(check(p1,p2,p3,p4,i)) ret+=dfs(pos-1,p2,p3,p4,i,flag && i==limit); //判断i是否可以和前面的数字共存。
27 }
28 if(!flag) dp[pos][p1][p2][p3][p4]=ret; //存值
29 return ret;
30 }
31 LL solve(LL x)
32 {
33 if(x<=0) return 0;
34 bo=0;
35 while(x)
36 {
37 bt[bo++]=x%10;
38 x/=10;
39 }
40 return dfs(bo-1,10,10,10,10,1);
41 }
42 int main()
43 {
44 while(scanf("%I64d%I64d%d",&L,&R,&K)!=EOF)
45 {
46 memset(dp,-1,sizeof(dp));
47 printf("%I64d
",solve(R)-solve(L-1));
48 }
49 return 0;
50 }