bzoj1026: [SCOI2009]windy数(传说你是数位DP)
1026: [SCOI2009]windy数
题目:传送门
题解:
其实之前年少无知的时候好像A过...表示当时并不知道什么数位DP
今天回来深造一发...
其实如果对这个算法稍有了解...看到这题的数据范围应该就YY出来了(蒟蒻博主表示很无力)
好吧讲做法:
这题的DP其实只是在于一开始的预处理:定义f[i][j]表示长度为i且最高位为j的windy数
那么转移方程再YY一发:
1 for(int i=0;i<=9;i++)f[1][i]=1; 2 for(int i=2;i<=10;i++) 3 for(int j=0;j<=9;j++) 4 for(int k=0;k<=9;k++) 5 if(abs(j-k)>=2) 6 f[i][j]+=f[i-1][k];
之后就是怎么利用这个f数组了,一个常规套路:
对于区间[a,b]的答案,如果我们可以求出1~b的答案和1~a-1的答案,那么输出[1~b]-[1~a-1]就是答案了啊。。
那么我的做法是用一个getsum函数,求出1~n-1的答案,最后两个区间相减就ok
具体看代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define qread(x) x=read() 7 using namespace std; 8 inline int read() 9 { 10 int f=1,x=0;char ch; 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 13 return f*x; 14 } 15 int A,B; 16 int f[11][11];//长度为i,最高位为j 17 int w[11]; 18 int getsum(int n)//求区间1~n-1的答案 19 { 20 int len=0,ans=0; 21 memset(w,0,sizeof(w)); 22 while(n!=0) 23 { 24 w[++len]=n%10; 25 n/=10; 26 } 27 for(int i=1;i<len;i++)//比自己至少小一位 28 for(int j=1;j<=9;j++)//枚举开头(所以不能为‘0’) 29 ans+=f[i][j]; 30 for(int i=1;i<w[len];i++)//和自己同样位数,但是最高位比自己小 31 ans+=f[len][i]; 32 for(int i=len-1;i>=1;i--)//最高位一样 33 { 34 for(int j=0;j<w[i];j++) 35 { 36 if(abs(j-w[i+1])>=2) 37 ans+=f[i][j]; 38 } 39 if(abs(w[i+1]-w[i])<2) 40 break; 41 } 42 return ans; 43 } 44 int main() 45 { 46 qread(A);qread(B);if(A>B)swap(A,B); 47 for(int i=0;i<=9;i++)f[1][i]=1; 48 for(int i=2;i<=10;i++) 49 for(int j=0;j<=9;j++) 50 for(int k=0;k<=9;k++) 51 if(abs(j-k)>=2) 52 f[i][j]+=f[i-1][k]; 53 int ans1=getsum(B+1); 54 int ans2=getsum(A); 55 //因为getsum求的是1~n-1这个区间的答案,所以我们输出getsum(B+1)-getsum(A); 56 printf("%d\n",ans1-ans2); 57 return 0; 58 }