Luogu P2657 windy数 题解报告

题目传送门

【题目大意】

定义不含前导零且相邻两个数字之差至少为2的数为$windy$数,求在$[A,B]$这个区间内存在多少$windy$数。

【思路分析】

好的据说这是一道数位DP板子题……$mark$一下,不过说实话这题难道不是记忆化搜索吗???QAQ

我们首先把问题转化成求$[1,B]$之间的$windy$数减去$[1,A-1]$之间的$windy$数,然后单独考虑。

设$f[i][j]$表示到第$i$位,前一位数字为$j$的方案数。然后我们为了保证数字不超出范围,要加一个变量记录是否有限制。有限制就意味着$j=a[i-1]$,那么当前第$i$位所放的数就不能超过$a[i]$,无限制就意味着$j<a[i]$,那么当前第$i$为所放的数就可以为$[0,9]$,当然要保证相邻数字之差不小于2。这里有一点要注意一下,就是当某一位数字为0时,相邻位置可以为0或1,所以我们可以把0看作是-2,这样可以保证没有情况被遗漏。

细节还是看代码叭QwQ

【代码实现】


 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #define g() getchar() 8 #define rg register 9 #define go(i,a,b) for(rg int i=a;i<=b;i++)10 #define back(i,a,b) for(rg int i=a;i>=b;i--)11 #define db double12 #define ll long long13 #define il inline14 #define pf printf15 #define mem(a,b) memset(a,b,sizeof(a))16 using namespace std;17 ll fr(){18 ll w=0,q=1;19 char ch=g();20 while(ch<0||ch>9){21 if(ch==-) q=-1;22 ch=g();23  }24 while(ch>=0&&ch<=9) w=(w<<1)+(w<<3)+ch-0,ch=g();25 return w*q;26 }27 ll A,B;28 int a[15],f[15][10];29 il int solve(rg int l,rg int lst,bool lim){30 if(!l) return 1;//如果已经到了最后一位,那么方案数为131 if(lst>0&&lim==0&&f[l][lst]!=-1) return f[l][lst];32 //记忆化,f数组记录的是没有限制的情况下的方案数33 rg int up=lim?a[l]:9,ans=0,nxt;//up记录上限34 go(i,0,up){35 if(abs(lst-i)<2) continue;36 nxt=i;37 if(lst==-2&&nxt==0) nxt=-2;38 ans+=solve(l-1,nxt,lim&&nxt==a[l]);39  }40 if(!lim&&lst>0) f[l][lst]=ans;//记录一下答案41 return ans;42 }43 il int work(ll x){44 rg int len=0;45 while(x) a[++len]=x%10,x/=10;46 return solve(len,-2,1);//因为数字是倒序记录的,所以倒序搜索47 }48 int main(){49 //freopen("","r",stdin);50 //freopen("","w",stdout);51 A=fr();B=fr();52 mem(f,-1);53 pf("%d\n",work(B)-work(A-1));54 return 0;55 }

代码戳这里

相关文章