BZOJ 1026: [SCOI2009]windy数
人生中的第一道数位dp,很有趣,虽然我很快推出了结构,但是过程却迟迟没有写出来,最后看别人的题解才恍然大悟
d[i][j]表示数位为i,最高位为j的方案数
DpInit非常简单,复杂度应该是O(10*log(n)),因为n的数位,也就是长度,可以写成len=(int)log10(n)+1
我们看一个例子
先别管两位数之间绝对值不能小于2这个条件
对于789456这个数
我们首先把f[1..len-1][1..9]加起来,存在ans里,就是1..99999的答案
接着,我们可以继续加f[len][1..6],而7不能加,因为以7开头的长度为len的答案太多了,会有一部分比789456大
继续,我们可以加f[len-1][0..7],f[len-2][0..8]……
这样就可以得到ans了。如果不明白请多读几遍或者看代码
然后再把这个条件加上,一句话的事,详见代码,我要赶制7月总结了
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 const int MAXN=15; 7 int a,b; 8 int f[MAXN][20]={0}; 9 inline int abs(int a){if (a>0) return a;else return -a;} 10 int get(int x){ //1~x-1 11 int ans(0),a[20]; 12 memset(a,0,sizeof(a)); 13 int len(0),X(x); 14 while (X){ 15 a[++len]=X%10; 16 X/=10; 17 } 18 for (int i=1;i<len;++i) 19 for (int j=1;j<=9;++j) 20 ans+=f[i][j]; 21 for (int j=1;j<a[len];++j) ans+=f[len][j]; 22 while (len--) { 23 for (int i=0;i<a[len];++i) 24 if (abs(a[len+1]-i)>=2) ans+=f[len][i]; 25 if (abs(a[len+1]-a[len])<2) return ans; 26 } 27 return ans; 28 } 29 int main(){ 30 scanf("%d%d",&a,&b); 31 int lena=log10(a)+1,lenb=log10(b)+1; 32 memset(f,0,sizeof(f)); 33 for (int i=0;i<=9;++i) f[1][i]=1; 34 for (int i=2;i<=lenb;++i) { 35 for (int j=0;j<=9;++j) 36 for (int k=0;k<=9;++k) 37 if (abs(k-j)>=2) f[i][j]+=f[i-1][k]; 38 } 39 printf("%d",get(b+1)-get(a)); 40 return 0; 41 }