P2657 [SCOI2009]windy数
P2657 [SCOI2009]windy数
题解
数位DP板子题
Solution 1
f[ i ][ j ] 表示长度为 i 的数字,最高温填的数字是 j 的时候,windy数的个数
f[ i ][ j ] = Σ f[ i ][ k ] ( abs(k-j)>=2 )
代码
#include<bits/stdc++.h> using namespace std; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } int aa,bb; int f[11][10]; //f[i][j] 从后往前数第i位填了数字j的windy数个数 //或者可以理解为长度为i的数字,最高位填了数字j的windy数个数 void pre() //预处理 { for(int i=0;i<=9;i++) f[1][i]=1; //单数位的数字也是windy数 for(int i=2;i<=10;i++) for(int j=0;j<=9;j++) for(int k=0;k<=9;k++) if(abs(j-k)>=2) f[i][j]+=f[i-1][k]; } int work(int x) //1~x-1中有多少个windy数 { int ans=0; int a[11],len=0; while(x) //存下x的各位数字 { a[++len]=x%10; x/=10; } for(int i=1;i<len;i++) for(int j=1;j<=9;j++) ans+=f[i][j]; //长度比x小的数字一定是合法的 for(int i=1;i<=a[len]-1;i++) ans+=f[len][i]; //最高位数字比x最高位小的也一定合法 for(int i=len-1;i>=1;i--) //从高位向低位枚举 { for(int j=0;j<=a[i]-1;j++) //x是上界,不可以超过x ,j是第i位上的数字 if(abs(j-a[i+1])>=2) ans+=f[i][j]; //和上一位比较,合法 if(abs(a[i+1]-a[i])<2) break; //上一位和自己比较,不合法,直接退出for循环 } return ans; } int main() { aa=read();bb=read(); pre(); printf("%d",work(bb+1)-work(aa)); return 0; }
Solution 2
dfs记忆化搜索实现 (实际是套路)
dp[ i ][ j ]表示填到 第 i 位 ,上一位填的数字是 j 的方案数,初始化 -1 ,表示都没有计算过
记 sum( i ) 表示 i 以内的windy数个数,ans=sum( b ) - sum( a-1 )
我们拿到一个数首先把它一位一位拆开放到数组里
填数字的时候考虑从最高位填到最低位(从低到高也行)
下面DFS记忆化搜索
代码
#include<bits/stdc++.h> using namespace std; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } int a,b,dp[15][15]; int c[15],len; int dfs(int pos,int pre,bool qdl,bool limit) //填到(该填)第pos位了,上一位填的数字是pre,qdl判断是否前面全是0,limit判断是否顶上界 { if(pos<=0) return 1; //已经填完了 if(!limit&&dp[pos][pre]!=-1) return dp[pos][pre]; //已经算过了,而且不顶上界合法 int res=0; int up=limit?c[pos]:9; //当前位置的上界(最大可以填到多少) for(int i=0;i<=up;i++) if(!qdl&&abs(i-pre)>=2||qdl) //1.所填的位置前面全是0,可以继续 //2.前面不是全位0,并且符合windy数(abs>=2),可以继续 res+=dfs(pos-1,i,qdl&&i==0,limit&&i==up); //位置后移,记录新pre,判断是否全为0,是否顶上界 if(!limit&&!qdl) dp[pos][pre]=res; //不顶上界不是全部前导零就记录答案 return res; } int sum(int x) { memset(c,0,sizeof(c)); len=0; memset(dp,-1,sizeof(dp)); while(x) { c[++len]=x%10; x/=10; } return dfs(len,0,1,1); } int main() { a=read();b=read(); printf("%d\n",sum(b)-sum(a-1)); return 0; }