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 double 12 #define ll long long 13 #define il inline 14 #define pf printf 15 #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;//如果已经到了最后一位,那么方案数为1 31 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 }