[SCOI2009]windy数
这是本蒟蒻做的第一道数位DP。。。(大佬勿喷QVQ
这个题据说比较裸?WTF我咋不知道 QVQ
emm。。。根据题意我们可以发现,通过枚举每个的位置的数我们可以递推出满足条件的数的数量
这里用 f[i][j] 表示 有 i 位 最高位的数是 j 有f[i][j]个方案
那么我们就可以 轻松 得到DP柿子啦~~~
注意:当我们得到A B这两个数的时候,我们要对 B 和 A-1 操作。。。
为什么A要-1呢?
因为我们得到的windy数是从 0~B 的那么我们重复的就是 0~A-1 的windy数
接下来要处理第二步?就是判断哪些答案合法
合法答案分为三部分:
假使我们的这个数为 X 有 K 位 一个数组 S 记录每位数是什么
first:位数小于 K 必然的这些数都小于X 所以我们把 f 数组无脑加上就好
second:位数等于 K 那么我们首先可以确定,最高位小于 S[K] 的答案肯定合法
third:下面就是最难搞的地方,我们要一位一位的判断答案的合法性
举个例子 19260817 135 我们倒着枚举每一位 for(int i=K-1;i>=1;i++);
我们可以发现对于第 i 位的数,我们可以通过枚举这个数 j,此时要满足两个条件。
小于 S[i] 而且 abs ( j - S[i] ) >= 2 那么选择这个数一定是合法的。
依次类推。。。但是,如果当 S[i+1] - S[i] < 2 时 那么以后的数一定是不合法的。。。
因为这两个数已经不满足条件了。。。
那么,我们的问题就解决了?
NO NO NO
当我们发现每一位都合法,就是这个数本身就是一个windy数那么我们的答案就应该+1
呆码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #define ll long long using namespace std; ll A,B,f[15][15]; // 多少位数 最高位是多少 有多少方案 int s[21]; inline void DP() { for(int i=0;i<=9;i++) f[1][i]=1; 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]; } inline ll solve(int x) { memset(s,0,sizeof(s)); if(x==0) return 0; int num=0; ll ans=0; while(x) { s[++num]=x%10; x/=10; } for(int i=1;i<=num-1;i++) for(int j=1;j<=9;j++) ans+=f[i][j]; for(int i=1;i<=s[num]-1;i++) ans+=f[num][i]; for(int i=num-1;i>=1;i--) { for(int j=0;j<=s[i]-1;j++) if(abs(j-s[i+1])>=2) ans+=f[i][j]; if(abs(s[i+1]-s[i])<2) break; if(i==1) ans+=1; } return ans; } int main() { cin>>A>>B; DP(); cout<<solve(B)-solve(A-1); }