zoukankan      html  css  js  c++  java
  • 判断正整数各个数位上的数字关系(算法)

    问题描述:

    示例:

    解题思路:

    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趋势的数据就是符合要求的。

  • 相关阅读:
    电信生命周期说明
    find in linux 2 微信公众号
    GDB中应该知道的几个调试方法 2 微信公众号
    linux 下程序员专用搜索源码用来替代grep的软件ack(后来发现一个更快的: rg), 且有vim插件的 2 微信公众号
    linux下的 c 和 c++ 开发工具及linux内核开发工具 2 微信公众号
    linux下命令行发送邮件的软件:mutt 微信公众号
    腺样体肿大的综合治疗考虑 微信公众号
    打呼噜治疗方法 微信公众号
    vim 操作 2 微信公众号
    nginx之外的web 服务器caddy 微信公众号
  • 原文地址:https://www.cnblogs.com/brifuture/p/9683643.html
Copyright © 2011-2022 走看看