判断正整数各个数位上的数字关系(算法)

问题描述:

示例:

 

解题思路:

  1. 首先解决输入的数据和各组数据的上下边界:
     1 // main method
     2 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
     3 String NStr = br.readLine();
     4 int N = Integer.parseInt( NStr );
     5 
     6 int[] aN = new int[ N ];
     7 int[] bN = new int[ N ];
     8 // 读取各组数据
     9 for( int i = 0; i < N; i++ ) {
    10     String[] data = br.readLine().split(" ");
    11     aN[i] = Integer.parseInt( data[0] );  // 区间的左边界
    12     bN[i] = Integer.parseInt( data[1] );  // 区间的右边界
    13 }
  2. 接下来对 [A, B] 区间进行遍历,对该区间内的所有数进行检查(调用 check 函数),如果返回的 score 比现有的 score 大,则更新现有的 score:

     1 public static int find( int A, int B ) {
     2     int score = 0;
     3 
     4     for( int n = A; n <= B; n++ ) {
     5         String nStr = String.valueOf( n );
     6         int tmpScore = check( nStr );  // 检查 n 字符串是不是 cc number,并且返回它的 score
     7         if( tmpScore > score )
     8             score = tmpScore;
     9     }
    10     return score;  // 返回的是 A-B 区间内的 CC Number 的最大 Score
    11 }
  3. 核心算法,检查这个数是否为 CC number,若是,则计算并返回它的 score:
     1 public static int check( String nStr ) {
     2     char cp, cb;  // pre, back
     3     int score = 0;
     4     int up1Start = -1, down1Start = -1, down1End = -1;
     5     int up2Start = -1, down2Start = -1;
     6     boolean isCCNumber = false;
     7     for( int j = 0; j < nStr.length() - 1; j++ ) {
     8         cp = nStr.charAt( j + 1 );
     9         cb = nStr.charAt( j );  // current char
    10 
    11         if( up1Start == -1 ) {
    12             if( cp > cb) {
    13                 // start up1
    14                 up1Start = j;
    15             }
    16         }
    17         else {
    18             if( down1Start == -1 ) {
    19                 // on up1
    20                 if( cp == cb ) {
    21                     up1Start = -1; // reset up1start
    22                 }
    23                 else if( cp < cb ) {
    24                     // start down1
    25                     down1Start = j;
    26                 }  // no need to process case cp > cb again
    27 
    28             }
    29             else {
    30                 if( down1End == -1 ) {
    31                     // on down1
    32                     if (cp >= cb) {
    33                         down1End = j; // finish down1
    34                     } // no need to process case cp < cb
    35                 }
    36                 else {
    37                     // check up-down second time
    38                     if( up2Start == -1 ) {
    39                         if( cp > cb && cb != '0' ) {
    40                             // start up2, cb can not be '0'
    41                             up2Start = j;
    42                             if( up2Start - down1End != 1 ) {
    43                                 up1Start = -1;
    44                                 down1Start = -1;
    45                                 down1End = -1;
    46                                 up2Start = -1;
    47                                 continue;
    48                             }
    49                         }
    50                     }
    51                     else {
    52                         if( down2Start == -1 ) {
    53                             // on up2
    54                             if( cp == cb ) {
    55                                 up2Start = -1; // reset up1start
    56                             }
    57                             else if( cp < cb ) {
    58                                 // start down2
    59                                 down2Start = j;
    60                                 isCCNumber = true;
    61                                 break; // no need to know where down2 ends
    62                             }  // no need to process case cp > cb again
    63 
    64                         } 
    65                         // else {
    66                         //    if( down2End == -1 ) {
    67                         //        // on down2
    68                         //        if (cp >= cb) {
    69                         //            down2End = j;
    70                         //        } // no need to process case cp < cb
    71                         //    }
    72                         // }
    73                     }
    74                 }
    75             }
    76         }
    77     }
    78     if( isCCNumber ) {
    79         score = calcScore( nStr );
    80     }
    81 
    82     return score;
    83 }

     

  4. 计算 score 的函数,只需要将该数字对应的字符串上每个字符与 '0' 字符的差值相加即可:
    1 public static int calcScore( String nStr ) {
    2     char c;
    3     int score = 0;
    4     for( int i = 0; i < nStr.length(); i++ ) {
    5         c = nStr.charAt( i );
    6         score += ( c - '0' );
    7     }
    8     return score;
    9 }

完整代码:

https://github.com/BriFuture/blog-code-example/blob/master/18-06to09/ccnumber/Main.java

核心步骤算法(check 方法)说明:

对于每个给定的数,要判断它是否为 CC number,有以下步骤:

从左往右(index 从 0 到 length - 1)依次判断相邻位数上的数字大小关系(循环),将当前数位上的数位 numCurr(对应代码中的 nb),下一个数位上的数标记为 numNext(对应代码中的 np)

每次循环从第 1 步开始:

  1. 若 up1Start 尚未被标记:
        - 判断 numNext > numCurr,若为 true 说明开始处于第一段 up 区域,将 up1Start 记为当前索引,执行第 2 步;
    若 up1Start 已被标记,执行第 2 步;

  2. 如果 down1Start 尚未被标记,判断 numNext 与 numCurr 的关系:
        - 若 numNext == numCurr,清除 up1Start 的标记,返回第 1 步;
        - 若 numNext < numCurr,将 down1Start 记为当前索引,执行第 3 步;   
    如果 down1Start 已被标记,执行第 3 步;

  3. 如果  down1End 尚未标记判断 numNext 与 numCurr 的关系:
        - 若 numNext >= numCurr,将 down1End 记为当前索引,执行第 4 步;
        - 否则继续执行第 3 步;
    如果  down1End 已被标记,执行第 4 步,

  4. 如果 up2Start 尚未标记,进行判断:
        - 若 numNext > numCurr 并且 numCurr 不为 0(C number 的最高位不能为 0),
          判断当前索引与 down1End 的差值,
             * 若差值不为 1,则需要重置所有标志位,执行第 1步,
             * 否则执行第 5 步;
        - 否则继续执行第 4 步;
    否则执行第 5 步;
  5. 如果 down2Start 尚未标记,判断 numNext 与 numCurr 的关系:
        若 numNext == numCurr,清除 up2Start 的标记,返回第 4 步;
        若 numNext < numCurr,将 down2Start 标记为当前索引,标记 isCCNumber 为 true,跳出循环;
        否则继续执行第 5 步;

上述步骤结束后,判断 isCCnumber,若为真的话说明这个数字是 CC number,计算它的 score 并返回,否则返回 0。

 

关于算法的思考,

做题的时候没有仔细思考这个题目,思路比较直接,其实这个位置的解法步骤有点复杂,实际上可以把它当做有限状态机来做:

 1 public enum State { Start, Stage1Up,Stage1Down, StageNext, Stage2Up, /*Stage2Down*/};
 2 public static int checkWithFSM( String nStr ) {
 3     State currState = State.Start;
 4     char cp, cb;  // pre, back
 5     int j = 0;
 6     while( j < nStr.length() - 1 ) {
 7         cp = nStr.charAt( j + 1 );  // next char
 8         cb = nStr.charAt( j );      // current char
 9         switch ( currState ) {
10             case Start:
11                 if( cp > cb )
12                     currState = State.Stage1Up;  // state State1Up
13                 else
14                     currState = State.Start;
15                 break;
16             case Stage1Up:
17                 if( cp == cb )
18                     currState = State.Start;  // reset
19                 else if( cp < cb )
20                     currState = State.Stage1Down; // end of Stage1Up
21                 // else Stage1Up
22                 break;
23             case Stage1Down:
24                 if( cp >= cb ) {
25                     currState = State.StageNext;  // end of Stage1Down
26                 }
27                 // else Stage1Down
28                 break;
29             case StageNext:
30                 if( cp > cb && cb != '0' )  // maximum number can not be '0'
31                     currState = State.Stage2Up;  // start  State2Up
32                 else
33                     currState = State.Start;  // Stage1Down 和 Stage2Up 不相邻,reset
34                 break;
35             case Stage2Up:
36                 if( cp == cb ) {
37                     currState = State.Start; // not on up, reset currState
38                 }
39                 else if( cp < cb ) {
40 //                        currState = State.Stage2Down; // start down
41                     return calcScore( nStr );
42                 }
43                 // else Stage2Up
44                 break;
45 //                case Stage2Down:  // no need to care about Stage2Down state
46 
47         }
48         j++;
49     }
50 
51     return 0;
52 }

换成 FSM 来做之后流程更加清楚了,具体细节也就不再赘述,流程图如下:

 

回顾问题:

将数字 n 上各个数位当作横坐标(离散取值,范围从 0 到 length - 1),各个数位的数字(0-9)当作纵坐标(离散取值,范围从 0 到 9),可以得到一个离散取点的二维平面图,平面图的走势为先增后减再增再减即可,

需要注意的是第一次下降结束的位置和第二次上升开始的位置重合:

如果连接各个离散的点,up-down,up-down趋势的数据就是符合要求的。

 

转载于:https://www.cnblogs.com/brifuture/p/9683643.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值