数位DP专题

这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪。

http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html

1 int dfs(int i, int s, bool e) {

2     if (i==-1) return s==target_s;

3     if (!e && ~f[i][s]) return f[i][s];

4     int res = 0;

5     int u = e?num[i]:9;

6     for (int d = first?1:0; d <= u; ++d)

7         res += dfs(i-1, new_s(s, d), e&&d==u);

8     return e?res:f[i][s]=res;

9 }

f为记忆化数组;

i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);

s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);

e表示之前的数是否是上界的前缀(即后面的数能否任意填)。

for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.

于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。

HDU2098 不要62

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 15

11 int dp[maxn][2],num[maxn];

12 int new_s(int s, int d)

13 {

14     if (d == 6) return 1;

15     else

16     {

17         return 0;

18     }

19 }

20 int dfs(int i, int s, bool e) {

21     if (i == -1) return 1;

22     if (!e && ~dp[i][s]) return dp[i][s];

23     int res = 0;

24     int u = e ? num[i] : 9;

25     for (int d = 0; d <= u; ++d)

26     {

27         if (d==4) continue;

28         if (s&&d==2) continue;

29         res += dfs(i - 1, new_s(s, d), e && d == u);

30     }

31     return e ? res : dp[i][s] = res;

32 }

33 int cal(int n)

34 {

35     int cnt = 0;

36     while (n)

37     {

38         num[cnt++] = n % 10;

39         n /= 10;

40     }

41     return dfs(cnt - 1, 0,1);

42 }

43 int main()

44 {

45     int l, r;

46     memset(dp, -1, sizeof(dp));

47     while (scanf("%d%d", &l, &r) != EOF&&l+r)

48     {

49         printf("%d\n", cal(r) - cal(l -1));

50     }

51     return 0;

52 }
View Code

HDU3555 Bomb

和上题差不多

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 30

11 typedef long long LL;

12 LL dp[maxn][3];

13 int num[maxn];

14 int new_s(int s, int d)

15 {

16     if (s == 2) return 2;

17     if (s == 1 && d == 9) return 2;

18     if (d == 4) return 1;

19     return 0;

20 

21 }

22 LL dfs(int i, int s, bool e)

23 {

24     if (i == -1) return s == 2;

25     if (!e&&~dp[i][s]) return dp[i][s];

26     LL ret = 0;

27     int u = e ? num[i] : 9;

28     for (int d = 0; d <= u; d++)

29         ret += dfs(i - 1, new_s(s, d), e&&d == u);

30     return e ? ret : dp[i][s] = ret;

31 

32 }

33 LL cal(LL n)

34 {

35     int cnt = 0;

36     while (n)

37     {

38         num[cnt++] = n % 10;

39         n /= 10;

40     }

41     return dfs(cnt - 1, 0, 1);

42 }

43 int main()

44 {

45     int T;

46     scanf("%d", &T);

47     memset(dp, -1, sizeof(dp));

48     while (T--)

49     {

50         LL n;

51         scanf("%I64d", &n);

52         printf("%I64d\n", cal(n));

53     }

54     return 0;

55 }
View Code

BZOJ1026 windy数

注意前导0

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 30

11 typedef long long LL;

12 LL dp[maxn][10];

13 int num[maxn];

14 LL dfs(int i, int s, bool e,int pre)

15 {

16     if (i == -1) return s == 1;

17     if (!e&&~dp[i][pre]&&s) return dp[i][pre];

18     LL ret = 0;

19     int u = e ? num[i] : 9;

20     for (int d = 0; d <= u; d++)

21     if(!s||abs(pre-d)>=2)

22     {

23         ret += dfs(i - 1, s||d>0, e&&d == u, d);

24     }

25     if (!e&&s)dp[i][pre] = ret;

26     return ret;

27 }

28     

29 LL cal(LL n)

30 {

31     int cnt = 0;

32     while (n)

33     {

34         num[cnt++] = n % 10;

35         n /= 10;

36     }

37     return dfs(cnt - 1, 0, 1,11);

38 }

39 int main()

40 {

41     LL x,y;

42     memset(dp, -1, sizeof(dp));

43     while (scanf("%lld%lld", &x,&y)!=EOF)

44     {

45         printf("%lld\n", cal(y)-cal(x-1));

46     }

47     return 0;

48 }
View Code

HDU3652 B-number

加一维,记录余数

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 30

11 typedef long long LL;

12 LL dp[maxn][15][3];

13 int num[maxn];

14 int new_s(int s, int d)

15 {

16     if (s == 2) return 2;

17     if (s == 1 && d == 3) return 2;

18     if (d == 1) return 1;

19     return 0;

20 }

21 LL dfs(int i, int s, bool e,int r)

22 {

23     if (i == -1)

24     {

25         if ((s== 2) && (r== 0)) return 1;

26         else return 0;

27     }

28     if (!e&&~dp[i][r][s]) return dp[i][r][s];

29     LL ret = 0;

30     int u = e ? num[i] : 9;

31     for (int d = 0; d <= u; d++)

32     {

33         ret += dfs(i - 1,new_s(s,d) , e&&d == u, (r*10+d)%13);

34     }

35     return e ? ret : dp[i][r][s] = ret;

36 }

37     

38 LL cal(LL n)

39 {

40     int cnt = 0;

41     while (n)

42     {

43         num[cnt++] = n % 10;

44         n /= 10;

45     }

46     return dfs(cnt - 1, 0, 1,0);

47 }

48 int main()

49 {

50     LL n;

51     memset(dp, -1, sizeof(dp));

52     while (scanf("%I64d",&n)!=EOF)

53     {

54         printf("%I64d\n", cal(n));

55     }

56     return 0;

57 }
View Code

HDU3943 K-th Nya Number

需要二分答案,还有就是注意区间范围是[P+1,Q],被坑了好多发,还有我很不解的就是,memset放在最外面就会WA,每输入一组案例清空一次就AC了。。坑爹。。

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 25

11 typedef long long LL;

12 LL dp[maxn][maxn][maxn];

13 LL P, Q, x, y;

14 int num[maxn];

15 LL dfs(int i, int sx, int sy, bool e)

16 {

17     if (i == -1)

18     {

19         if (sx == x&&sy == y) return 1; 

20         else return 0;

21     }

22     if (!e&&~dp[i][sx][sy]) return dp[i][sx][sy];

23     LL ret = 0;

24     int u = e ? num[i] : 9;

25     for (int d = 0; d <= u; d++)

26     {

27         if (sx == x&&d == 4) continue;

28         if (sy == y&&d == 7) continue;

29         int a, b;

30         a = sx; b = sy;

31         if (d == 4) a++;

32         if (d == 7) b++;

33         ret += dfs(i - 1, a, b, e&&d == u);

34     }

35     return e ? ret : dp[i][sx][sy] = ret;

36 }

37 LL cal(LL n)

38 {

39     int cnt = 0;

40     while (n)

41     {

42         num[cnt++] = n % 10;

43         n /= 10;

44     }

45     return dfs(cnt - 1, 0, 0, 1);

46 }

47 LL Bin(LL k)

48 {

49     LL l, r, mid, ans,ret;

50     ans = 0;

51     ret = cal(P);

52     l = P+1; r = Q;

53     while (l <= r)

54     {

55         mid = (l + r) >> 1;

56         if (cal(mid) - ret>= k)

57         {

58             ans = mid;

59             r = mid - 1;

60         }

61         else l = mid + 1;

62     }

63     return ans;

64 }

65 int main()

66 {

67     int T,kase=0;

68     scanf("%d", &T);

69     

70     while (T--)

71     {

72         int n;

73         memset(dp, -1, sizeof(dp));

74         scanf("%I64d%I64d%I64d%I64d", &P, &Q, &x, &y);

75         scanf("%d", &n);

76         printf("Case #%d:\n", ++kase);

77         while (n--)

78         {

79             LL k;

80             scanf("%I64d",&k);

81             LL ans = Bin(k);

82             if (ans)

83             printf("%I64d\n", ans);

84             else printf("Nya!\n");

85         }

86     }

87     return 0;

88 }
View Code

POJ3208 Apocalypse Someday

做法和上题一样,需要注意的是它是必须有连续的三个6,还有就是二分的上界尽量大。。

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 30

11 typedef long long LL;

12 LL dp[maxn][4];

13 int num[maxn];

14 int new_d(int s, int d)

15 {

16     if (s == 3) return 3;

17     int st = s;

18     if (d == 6) s++;

19     return st==s?0:s;

20 }

21 LL dfs(int i, int s,bool e)

22 {

23     if (i == -1) return s == 3;

24     if (!e&&~dp[i][s]) return dp[i][s];

25     LL ret = 0;

26     int u = e ? num[i] : 9;

27     for (int d = 0; d <= u; d++)

28     {

29         ret += dfs(i - 1, new_d(s,d),e&&d == u);

30     }

31     return e ? ret : dp[i][s] = ret;

32 }

33 LL cal(LL n)

34 {

35     int cnt = 0;

36     while (n)

37     {

38         num[cnt++] = n % 10;

39         n /= 10;

40     }

41     return dfs(cnt - 1, 0, 1);

42 }

43 LL Bin(LL k)

44 {

45     LL l, r, mid, ans,ret;

46     ans = 0;

47     l = 666, r = 100000000000LL;

48     while (l <= r)

49     {

50         mid = (l + r) >> 1;

51         if (cal(mid)>= k)

52         {

53             ans = mid;

54             r = mid - 1;

55         }

56         else l = mid + 1;

57     }

58     return ans;

59 }

60 int main()

61 {

62     int T;

63     scanf("%d", &T);

64     while (T--)

65     {

66         LL k;

67         memset(dp, -1, sizeof(dp));

68         scanf("%I64d", &k);

69         printf("%I64d\n", Bin(k));

70     }

71     return 0;

72 }
View Code

SPOJ BALNUM Balanced Numbers

刚开始一直不知道该怎么记录前面的状态,搜了下解题报告,用的是三进制来表示前面的状态(此题的精华就是这里吧。。),因为状态总数为3^10,因此也不大。

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 66666

11 typedef long long LL;

12 LL dp[25][maxn];

13 int num[25];

14 int cnt[10];

15 void go(int s)

16 {

17     for (int i = 0; i < 10; i++)

18     {

19         cnt[i] = s % 3;

20         s /= 3;

21     }

22 }

23 int new_s(int s, int d)

24 {

25     go(s);

26     if (cnt[d] == 0) cnt[d] = 1;

27     else

28         cnt[d] = 3 - cnt[d];

29     int base = 1;

30     s = 0;

31     for (int i = 0; i < 10; i++)

32     {

33         s += base * cnt[i];

34         base *= 3;

35     }

36     return s;

37 }

38 int check(int s)

39 {

40     go(s);

41     for (int i = 0; i < 10; i++)

42     {

43         if ((i & 1) && (cnt[i] == 1)) return 0;

44         if (!(i & 1) && (cnt[i] == 2))return 0;

45     }

46     return 1;

47 }

48 LL dfs(int i, int s, bool e,int zero)

49 {

50     if (i == -1) return check(s);

51     if (!e&&~dp[i][s]) return dp[i][s];

52     LL ret = 0;

53     int u = e ? num[i] : 9;

54     for (int d = 0; d <= u; d++)

55     {

56         ret += dfs(i - 1, zero&&d==0?0:new_s(s, d), e&&d == u,zero&&d==0);

57     }

58     return e ? ret : dp[i][s] = ret;

59 }

60 LL cal(LL n)

61 {

62     int cnt = 0;

63     while (n)

64     {

65         num[cnt++] = n % 10;

66         n /= 10;

67     }

68     return dfs(cnt - 1, 0, 1,1);

69 }

70 int main()

71 {

72     int T;

73     scanf("%d", &T);

74     memset(dp, -1, sizeof(dp));

75     while (T--)

76     {

77         LL x, y;

78         scanf("%lld%lld", &x, &y);

79         printf("%lld\n", cal(y) - cal(x - 1));

80     }

81     return 0;

82 }
View Code

SPOJ MYQ10 Mirror Number

每个数字只可能是0,1,8,区间比较大0 <= a<=b <= 10^44,所以输入要用字符串,一般我们求答案都是:cal(b)-cal(a-1),但此题是字符串,因此需要特殊下a是不是Mirror Number,还被坑了好久的就是,由于是字符串输入,最高位的下标是0。。然后我没有反转过来。。找了好久才发现。。用一个数组记录前面选的数(之前的状态),只要第一个非0位确定就可以知道回文串的长度,也就知道回文串中心的位置,然后从中心更低的位置开始判断是不是回文。前导0也需要注意

 1 #include <iostream>

 2 #include <algorithm>

 3 #include <cstdio>

 4 #include <cstring>

 5 #include <utility>

 6 #include <vector>

 7 #include <queue>

 8 using namespace std;

 9 #define INF 0x3f3f3f3f

10 #define maxn 50

11 typedef long long LL;

12 LL dp[maxn][maxn];

13 int num[maxn],tmp[maxn];

14 LL dfs(int i, int len, bool e,int zero)

15 {

16     if (i == -1) return 1;

17     if (!e&&~dp[i][len]) return dp[i][len];

18     LL ret = 0;

19     int u = e ? num[i] : 9;

20     for (int d = 0; d <= u; d++)

21     {

22         if (d!=0&&d!=1&&d!=8) continue;

23         if (zero)

24         {

25             tmp[i] = d;

26             ret += dfs(i - 1, len - !d, e&&d == u, zero&& d == 0);

27         }

28         else

29         {

30             int mid = len / 2;

31             int fg = i < mid ? 1 : 0;

32             if (fg)

33             {

34                 if (tmp[len - i - 1] == d)

35                     ret += dfs(i - 1, len, e&&d == u, zero);

36             }

37             else

38             {

39                 tmp[i] = d;

40                 ret += dfs(i - 1, len, e&&d == u, zero);

41             }

42         }

43     }

44     return e ? ret : dp[i][len] = ret;

45 }

46 LL cal(char *s)

47 {

48     int cnt = strlen(s);

49     for (int i = 0; i < cnt; i++) num[cnt-i-1] = s[i] - '0';

50     return dfs(cnt - 1, cnt, 1,1);

51 }

52 int ok(char *s)

53 {

54     int len = strlen(s);

55     for (int i = 0; i < len; i++)

56         if (s[i] != '0'&& s[i] != '8'&&s[i] != '1') return 0;

57     for (int i = 0; i < len / 2; i++)

58         if (s[i] != s[len - i - 1]) return 0;

59     return 1;

60 }

61 int main()

62 {

63     int T;

64     scanf("%d", &T);

65     memset(dp, -1, sizeof(dp));

66     while (T--)

67     {

68         char x[maxn], y[maxn];

69         scanf("%s%s", x, y);

70         printf("%lld\n", cal(y) - cal(x)+ok(x));

71     }

72     

73     return 0;

74 }
View Code

 

你可能感兴趣的:(dp)