POJ 3208 Apocalypse Someday

题意:

   将含有连续的三个6的数称为不吉利数,比如666,1666,6662,但是6266吉利。则666为第一个不吉利数,输入整数n,求第n个不吉利数。(n <= 5*10^7)

解法:

   如果是给出n,求n以内的不吉利数有多少个,就是一个普通的数位DP。所以,就二分答案,对于每一个mid,求一下小于等于mid的数中不吉利数的个数。

tag:二分法,数位DP

  1 /*
  2  * Author:  Plumrain
  3  * Created Time:  2013-12-14 20:55
  4  * File Name: DP-POJ-3208.cpp
  5  */
  6 #include <iostream>
  7 #include <cstdio>
  8 #include <cstring>
  9 
 10 using namespace std;
 11 
 12 #define CLR(x) memset(x, 0, sizeof(x))
 13 typedef long long int64;
 14 int64 d1[20][10][10], d2[20][10][10];
 15 
 16 int64 get_num(int64 x)
 17 {
 18     int len = 0, dit[30];
 19     bool xxx = 0;
 20     while (x){
 21         dit[len++] = x % 10;
 22         x /= 10;
 23         if (len > 2 && dit[len-1] == 6 && dit[len-2] == 6 && dit[len-3] == 6)
 24             xxx = 1;
 25     }
 26     dit[len] = 0; dit[len+1] = 0;
 27     
 28     int64 ret = 0;
 29     if (xxx) ret = 1;
 30     bool flag = 0;
 31     for (int i = len-1; i >= 0; -- i){
 32         for (int j = 0; j < 10; ++ j)
 33             for (int k = 0; k < 10; ++ k)
 34                 ret += dit[i] * d2[i][j][k];
 35 
 36         if (flag){
 37             for (int j = 0; j < 10; ++ j)
 38                 for (int k = 0; k < 10; ++ k)
 39                     ret += dit[i] * d1[i][j][k];
 40         }
 41         else {
 42             if (dit[i+2] == 6  && dit[i+1] == 6 && dit[i] > 6){
 43                 for (int j = 0; j < 10; ++ j)
 44                     for (int k = 0; k < 10; ++ k)
 45                         ret += d1[i][j][k];
 46             }
 47             else if (dit[i+1] == 6 && dit[i] > 6){
 48                 for (int j = 0; j < 10; ++ j)
 49                     ret += d1[i][6][j];
 50             }
 51             else if (dit[i] > 6) ret += d1[i][6][6];
 52         }
 53 
 54         if (dit[i] == 6 && dit[i+1] == 6 && dit[i+2] == 6)
 55             flag = 1;
 56     }
 57     return ret;
 58 }
 59 
 60 int64 gao(int64 n)
 61 {
 62     int64 l = 0, r = 100000000000000;
 63     while (l <= r){
 64         int64 mid = (l + r) >> 1;
 65         if (get_num(mid) < n) l = mid + 1;
 66         else r = mid - 1;
 67     }
 68     return l;
 69 }
 70 
 71 int main()
 72 {
 73     CLR (d1); CLR (d2);
 74     d1[0][0][0] = 1;
 75     for (int i = 0; i < 10; ++ i){
 76         d1[1][i][0] = 1;
 77         for (int j = 0; j < 10; ++ j)
 78             d1[2][i][j] = 1;
 79     }
 80 
 81     for (int i = 3; i < 15; ++ i){
 82         d2[i][6][6] += d1[i-1][6][6];
 83         d1[i][6][6] -= d1[i-1][6][6];
 84         for (int j = 0; j < 10; ++ j)
 85             for (int k = 0; k < 10; ++ k)
 86                 for (int l = 0; l < 10; ++ l){
 87                     d2[i][j][k] += d2[i-1][k][l];
 88                     d1[i][j][k] += d1[i-1][k][l];
 89                 }
 90     }
 91 
 92     int T;
 93     scanf ("%d", &T);
 94     while (T--){
 95         int64 n;
 96         cin >> n;
 97         cout << gao(n) << endl;
 98     }
 99     return 0;
100 }
View Code

 

转载于:https://www.cnblogs.com/plumrain/p/POJ_3208.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值