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 }
bzoj1026

 

posted on 2016-07-31 21:24  Chuckqgz  阅读(179)  评论(0编辑  收藏  举报

导航