【BZOJ】1026 [SCOI2009]windy数
【算法】数位DP
【题解】参考题解
记忆化搜索挺好写的,而且比DP+递推快。
大概思路是记录h(高度),pre(前一位数字),limit(限制)。
从根往叶子走,limit=0时,扫0~9判断符合条件就递归。
limit=1时,也就是当前位于n上,只能扫0~end-1,end就要limit=1来递归。
过程中记录一下f数组防止复算。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int f[20][20],l,r,a[20]; int dfs(int h,int pre,int limit) { if(h==-1)return 1; if(pre!=-1&&!limit&&f[h][pre]!=-1)return f[h][pre]; int end=limit?a[h]:9;int ans=0; for(int i=0;i<=end;i++) { if(pre==-1&&i==0)ans+=dfs(h-1,-1,end==0?1:0); else if(abs(i-pre)>=2||pre==-1) { if(i==end&&limit)ans+=dfs(h-1,i,1); else ans+=dfs(h-1,i,0); } } if(!limit)f[h][pre]=ans; return ans; } int solve(int x) { if(x==0)return 1; int n=0; while(x>0)a[n++]=x%10,x/=10; return dfs(n-1,-1,1); } int main() { scanf("%d%d",&l,&r); memset(f,-1,sizeof(f)); printf("%d",solve(r)-solve(l-1)); return 0; }
补一个递推,特别注意逐位确定的时候判断合法。
#include<cstdio> #define ll long long int abs(int x){return x>=0?x:-x;} ll f[20][20]; int A,B,a[20]; int solve(int x){ x++; int len; for(len=1;x;len++){ a[len]=x%10; x/=10; } len--; ll ans=0; for(int i=len-1;i>=1;i--)for(int j=1;j<=9;j++)ans+=f[i][j]; for(int i=len;i>=1;i--){ for(int j=(i==len);j<a[i];j++)if(abs(j-a[i+1])>=2||i==len){ ans+=f[i][j]; } if(i!=len&&abs(a[i]-a[i+1])<2)break; } return ans; } int N; int main(){ scanf("%d%d",&A,&B);N=10; for(int j=0;j<=9;j++)f[1][j]=1; for(int i=2;i<=N;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]; } } } printf("%d",solve(B)-solve(A-1)); return 0; }