2013年微软实习生招聘笔试题目(c/c++你懂多少?)

        文章最初出处(原创): http://ilovers.sinaapp.com/drupal/article/2013年微软实习生招聘笔试题目

今天本是清明放假,但是,但是微软就是在今天下午组织了笔试考试,据说是全国统考。75分钟,20 道选择题,问题是,问题是神马?先看下简单的评分:

1-8      3     2    -2     0
9-18     5     3    -3     0
19-20    13    7    -7     0

1-8 每题 3 分,完全正确 3 分,不完全正确但是没有错误 2 分,有错误 -2 分,不做 0 分;19、20 题,完全正确 13 分,有错就是 -7 分,不做 0 分。是的,么有看错,有负分!做错了,不仅意味着本道题的分不得,而且要倒扣,简单滴说,最后一题做错了,就会从 100 分中扣去 20 分,因为本题得分 -7 分!如果题目全部都错做了,估计是 -50 多分!

题目给 me 的赶脚是,非大牛不能得高分,像 me 们这些半斤八两的,还不敢轻易选择,估计多数都是半对,实际上得一半的分。废话不多说,上题(题目是 me 翻译过来的,无碍大局的地方有所修改,所以不算原模原样的题)。me 会将 me 认为的正确答案标注为蓝颜色,而 me 选的答案标注为红颜色(me 没有标准和正确答案),后面加上题目分析。

笔试题目

  1. 下面哪些调用转换支持可变长度参数:( 3 分 )
    A. cdecl B. stdcall C. pascal D. fastcall
    注:me 表示丫梨很大,第一题不会,~~~~(>_<)~~~~

    分析:关于上面的函数修饰符的详细说明,可以看看百度百科。这里也简单说明,cdecl 是 C 语言的调用方式,函数参数从右到左求值,然后入栈,参数由调用方负责清理,传多少参数其实都无所谓的,于是就支持所谓的可变长度的参数;stdcall 是 C++ 的调用方式,参数从右到左求值,入栈,由被调用方处理参数,所以参数个数类型都必须匹配;pascal 是对 PASCAL 函数的调用方式,参数自左向右求值,其他类似于 stdcall;而 fastcall 的情况忽略。

  2. 下面程序执行结果:( 3 分 )
    1. #include
    2. using  namespace std ;
    3.  
    4. class A {
    5. public :
    6.      virtual  void f ( )  {    cout  <<  "A::f() " ;  }
    7.      void f ( )  const  {    cout  <<  "A::f() const " ;  }
    8. } ;
    9.  
    10. class B  :  public A
    11. {
    12. public :
    13.      void f ( )  {    cout  <<  "B::f() " ;  }
    14.      void f ( )  const  {    cout  <<  "B::f() const " ;  }
    15. } ;
    16.  
    17. void g ( const A * a )
    18. {
    19.     a - >f ( ) ;
    20. }
    21.  
    22. int main ( int argc,  char  *argv [ ] )
    23. {
    24.     A * p  =  new B ( ) ;
    25.     p - >f ( ) ;
    26.     g (p ) ;
    27.      delete (p ) ;
    28.    
    29.      return  0 ;
    30. }

    A. B::f() B::f() const
    B. B::f() A::f() const
    C. A::f() B::f() const
    D. A::f() A::f() const

    分析:用 const 来修饰函数和没用 const 是不同的重载,const 修饰函数表明,函数不能修改对象的状态/值。对于非 const 对象调用非 const 函数,当然也可以调用 const 函数(优先前者);然而 const 对象只能调用 const 函数。其次,A 中的 void f() 标注为 virtual ,那么子类 B 中跟它原模原样声明即使米有标注为 virtual 的函数依然是 virtual 函数。virtual 函数就是通常所谓的实现多态的方法了,比如,上面的 p->f(); p 虽然是 A 类型指针,但是实际上指向 B 对象,调用的时候调用的是 B 的 f() 方法。为嘛 a->f() 不调用 B 的 f() const 方法呢?c++ 除非标注为 virtual,否则不进行多态,也就是说 a 是 A 类型的指针,就调用 A 对应的 f() const 方法。
    问题并没有到此结束,上面的代码就是有 bug 的,试问,delete p; 的时候删除对象,是作为 A 对象删呢,还是 B 对象删呢?p 的类型是 A*,但是指向的是 B 对象丫,好吧,这里应该使用多态,正确的做法应该将 A 的析构函数标注为 virtual,否则,程序就有 undefinde behaviour(未定义的行为)。继续讨论,看下面的 code :

    1. int main ( int argc,  char  *argv [ ] )
    2. {
    3.     A base  = B ( ) ;
    4.     base. f ( ) ;
    5.     g ( &base ) ;
    6.  
    7.      return  0 ;
    8. }

    这里的结果会和上面的一样么?!——,实际情况是神马呢,——,不一样!这个时候的结果选 D,理由:多态只能通过指针和引用使用,对象不能使用。说到引用,将上面的代码修改为 A& base = B(); 会怎么样呢?编译错误!引用不能引用临时对象!所以代码只能改为 B b; A& base = b; 比如下面的:

    1. int main ( int argc,  char  *argv [ ] )
    2. {
    3.     B b ;     // B b();
    4.     A & base  = b ;
    5.     base. f ( ) ;
    6.     g ( &base ) ;
    7.  
    8.      return  0 ;
    9. }

    这个时候程序的结果还是 B。定义 b 的时候,使用上面注释掉的行不行呢?不行,因为那样,编译器会认为 b 是一个函数!!what?!!默认构造对象的时候,() 是不能加的??!!好吧,就是这样。再进一步说,会不会赶脚上面的代码有点“蹩脚”?如果 A a = B(); 就是可以的,然而 A& a = B(); 不可以,而要首先去定义一个 B对象,然后去引用,麻烦!好吧,c++11给 me 们带来了新希望(如果 u 不认为是灾难的话):

    1. int main ( int argc,  char  *argv [ ] )
    2. {
    3.     A && base  = B ( ) ;
    4.     base. f ( ) ;
    5.     g ( &base ) ;
    6.  
    7.      return  0 ;
    8. }

    这里的结果的答案还是 B。A&& 是右值引用,使得 me 们可以引用到一个临时量 B(); 自此以后那个临时量就归右值引用管了,不再算是临时量。基本到此为止,有点混乱,有点O__O"…。再最后补充一句,少用指针,多用对象和引用!(使用引用的时候,析构函数貌似不需要显式地标注为 virtual,这一点细节有点不清楚,至少编译器对此没有提示。)

  3. 链表和数组的区别: ( 3 分 )
    A. 在有序的情况下搜索
    B. 插入和删除
    C. 随机访问

    D. 数据存储类型

    分析:ABC 是明显的,有序的情况下数组可以二分查找,而链表不可以;插入和删除,数组不如链表灵活;链表不如数组可以随机访问。D 选项,不敢选,me 数据结构感觉跟存储类型么有必然关系,如果说存储类型指的是栈 (stack) 或是堆 (heap) 的话,数组也可以在堆上分配,链表也可以是静态链表(栈上的空间)。

  4. Windows 下进程和线程的描述,哪些是对的:( 3 分 )
    A. 操作系统的一个程序必须有一个进程,但是不必须有一个线程
    B. 进程有自己的栈空间,而线程只共享父进程的栈空间
    C. 线程必从属于一个进程
    D. 线程可以更改从属的进程

    分析:最初 me 不确定有些说法,因为题目强调说的是 Windows 下的进程和线程,不过后来翻看了下书,基本确定了某些说法。Windows 中的进程和线程的概念和一般操作系统书中的说法是一致的,进程是资源分配的基本单位,而线程是 CPU 调度的基本单位;一个线程从属于一个进程,当然一个进程可以创建多个线程,一个进程中的多个线程共享进程的栈空间(当然也有其他一些资源);但是每一个线程也可以有自己的栈空间,叫 TLS(线程本地存储);如果一个进程中没有显式地创建一个线程,那么就是所谓的单线程进程,其实也就是 main 执行流对应的线程。这样的话,A、B 是错的,C 是对的,而线程可以更改所属进程,太扯了,错误!补充一点,记得以前看 UNIX 高级编程,貌似有这样的说法:UNIX 系统的进程和线程和前面的说法也基本一致,但是 Linux 却特殊一点,特殊到哪一点呢?似乎没有进程和线程的明确划分,都是线程,只不过有些线程就是共享一些数据(类似于线程共享进程的数据),给人的赶脚就是同属于一个进程;有些线程不同享数据,就如同进程一般有所区分。(这里的说法不一定准确,所以,O__O"…)

  5. 下面代码段的运行结果:( 3 分 )
    1. #include
    2.  
    3. int main ( )
    4. {
    5.      int x  =  10 ;
    6.      int y  =  10 ;
    7.  
    8.     x  = x ++ ;
    9.     y  =  ++y ;
    10.  
    11.      printf ( "%d %d", x, y ) ;
    12.  
    13.      return  0 ;
    14. }

    A. 10 10 B. 10 11 C. 11 10 D. 11 11

    分析:神奇的题目有木有丫!按标准的说法,像 x=x++; 这样的表达式,是 undefined !就是标准中没有定义的。因为 x++ 表达式的值是 10 ,后来要赋值给 x,其次 x 还要自加 1,问题是,先赋值呢,还是先自加呢?这里没有统一的说法!虽然 y = ++y; 不管怎么说结果都一样,me 认为这样的表达式也不是很合理!在一个表达式中对同一个变量多次赋值和多次使用,结果很难把握。
    说说运行结果:多数人在 vc++ 和 gcc 下运行结果都是 D,但是 me 的运行结果是 B,me 的系统,win7 64位,使用 Mingw-w64 gcc 4.8.0 编译器。

  6. C# 或是 Java 程序段的结果:( 3 分 )
    1. int [ ] [ ] array  =  new  int [ 3 ] [ ] {
    2.      new  int [ 3 ] { 5, 6, 2 },
    3.      new  int [ 5 ] { 6, 9, 7, 8, 3 },
    4.      new  int [ 2 ] { 3, 2 }
    5. } ;

    array[2][2] 返回神马 ?
    A. 9 B. 6 C. 2 D. 溢出

    分析:介个,貌似不用多说,跟 C 中的数组有所区分,C 中的二维数组就是相同一维数组的数组,结构比较整齐,而 Java/C# 中的二维数组仅仅是一维数组的数组而已,所以 array[2][2] 这个元素其实不存在,在 Java 中运行会抛出越界异常。(实际上面的代码段不是合法的 Java 代码段,不过意思是那样的。)

  7. 下面说法哪些正确?( 3 分 )
    A. const int a; // a 是常数
    B. int const a; // a 是常数
    C. int const *a; // a 指向常数的指针

    D. const int *a; // a 是常指针
    E. int const *a; // a 是常指针

    分析:关于 const 和 static 貌似永远有说不完的话题,O__O"…。A、B、C 都是正确的,D 和 E 都是指向常量的指针,const 放置到 int 前面还是后面不是问题所在,而 int * const a; 才是常量指针,可以修改指向的值,但是不能修改指向。实际话题还可以再深入些,比如 A 的声明是合法的 C++ 声明吗?是合法的 C 声明吗?在 C++ 中 A 的写法肯定是 error,因为没有初始化!而在 C 中,A 只是声明而已,就是说明它是一个 const int,maybe 在其他处定义了。下面的代码段:

    1. int  const a  =  3 ;
    2. int  const a ;
    3.  
    4. int main ( int argc,  char  *argv [ ] )
    5. {
    6.      return  0 ;
    7. }

    c 程序上面是合法程序,c++ 是错误程序!如果 typedef int *PtrInt; 这种情况下 const PtrInt a; a 是常量指针呢,还是指向常量呢?—— 可以自己去尝试一下。(答案:常量指针。)

  8. 下面程序的执行结果:( 3 分 )
    1. #include
    2.  
    3. class A {
    4. public :
    5.      long a ;
    6. } ;
    7.  
    8. class B  :  public A
    9. {
    10. public :
    11.      long b ;
    12. } ;
    13.  
    14. void seta (A * data,  int idx )
    15. {
    16.     data [idx ]. a  =  2 ;
    17. }
    18.  
    19. int main ( int argc,  char  *argv [ ] )
    20. {
    21.     B data [ 4 ] ;
    22.  
    23.      for ( int i = 0 ; i < 4 ;  ++i ) {
    24.         data [i ]. a  =  1 ;
    25.         data [i ]. b  =  1 ;
    26.         seta (data, i ) ;
    27.      }
    28.  
    29.      for ( int i = 0 ; i < 4 ;  ++i ) {
    30.         std :: cout   << data [i ]. a  << data [i ]. b ;
    31.      }
    32.  
    33.      return  0 ;
    34. }

    A. 11111111 B. 12121212 C. 11112222 D. 21212121

    分析:程序的运行结果是 22221111,这里没有答案!至于答案的分析,应该涉及到 c/c++ 内存对象的布局问题,虽然 me 不大懂,但是还是可以“想当然”滴班门弄斧一下。像上面的类 A,和 c 中的结构体是兼容的,在 c++ 中叫做 POD,结构体中数据布置,应该是按照声明的顺序放置的(中间可能有空隙),所谓名字的访问其实也是根据顺序访问的,看个下面的例子:

    1. #include
    2.  
    3. struct Test {
    4.      int a ;
    5.      int b ;
    6. } ;
    7.  
    8. int main ( int argc,  char  *argv [ ] )
    9. {
    10.      long  long test  =  0x1234567887654321 ;
    11.  
    12.      printf ( "%x %x"( * ( struct Test * ) &test ). a( * ( struct Test * ) &test ). b ) ;     // 87654321 12345678
    13.  
    14.      return  0 ;
    15. }

    int 是 4 个字节,long long 是 8 个字节,强制将 long long 的 test 当做结构体 struct Test 处理,.a 访问的是低 4 个字节的内容,.b 访问的是高 4 个字节的内容。有了这点基础,再来看原来的题目,A 是一个类(结构体),里面只容纳一个 long,(me 电脑上 4 个字节),B 继承了 A,又多容纳了一个 long,实际就是俩 long。data 的 B 类型数组 4 个对象,本来是 8 个 long,不看 seta 函数的话,全部是 1,而在函数 seta 中处理的时候,数组变成 A 类型的了,一个 A 元素是一个 long,所以在函数 seta 内部,访问 data[0] - data[3] 实际是访问的 8 个 long 的前 4 个 long,也就是将前 4 个 long 置为 2,前 4 个 long 在 main 函数中对应的就是 B 类型数组的 data[0].a、data[0].b、data[1].a、data[1].b,于是乎 main 函数输出 B 数组对象,结果是 22221111。

  9. 1000 个瓶子中有一瓶毒药,一只老鼠吃到毒药一周之内会死,如果要在一周之内检测出有毒药的一瓶,问至少需要几只老鼠?( 5 分 )
    A. 9 B. 10 C. 32 D. 999 E. 上面答案都不对

    分析:当初图样图森破,图拿衣服。老鼠吃过药不会立马死,当时 me 这个重点么抓到,其次,要测出来这瓶毒药至少几只老鼠,me 当时傻×:最少一只吧,比如正好碰巧测出来!~~~~(>_<)~~~~好吧,网上这个题目有很多人已经回答了,10 只老鼠,因为每只老鼠的死亡状态可能有 2 种,10 只的话,可以拼凑出 2 ^ 10 = 1024 种状态,也就是 1024 只瓶子的情况都可以测出来,至于细节,下面叙述。

    0 - 1023 的话,用二进制表示正好是 10 位,每一位都是 0 或是 1。如果第一位表示第一只老鼠吃不吃的情况,第二位表示第二只老鼠吃不吃的情况,第 n 位表示第 n 只老鼠吃不吃的情况,首先说,这 1024 个瓶子,正好可以给老鼠们一种吃法,现在的问题是,比如第 k 瓶有毒,老鼠们肯定有一种状态吧,比如有些死了,有些没有死,我们希望第 k 瓶有毒就对应一种状态,这样的话,根据状态就可以反推是第 k 瓶。现在就看是不是有这么个一一对应关系。
    比如第 1 瓶有毒,0000...0001,第一只老鼠吃了,其他的老鼠都没有吃,如果有毒的话,第一只老鼠就死了,其他老鼠都没事(注:其他瓶没有毒,所以即使第一只老鼠吃了其他瓶,其他老鼠也吃了其他的瓶,但是它们的命运却不改变,就因为第 1 瓶有毒!);再比如第 3 瓶有毒,0000...0011,第一、二只老鼠吃了,其他老鼠没有吃,结果第一和第二只老鼠死了,其他老鼠没事(注:它们有没有吃其他的瓶子也不影响它们的命运,因为第 3 瓶有毒!)。由此可见,第 k 瓶有毒,就是 k 对应的二进制数的二进制位为 1 的老鼠死掉,这是必然的。一瓶毒药的方法,比如第 k 瓶,对应一个二进制数,对应一种老鼠死法,所以,一种老鼠死法,就对应一瓶毒药的方法,也就是根据老鼠的死法,就能判断出是第多少瓶:

    将第 i 只老鼠死了,记二进制的第 i 位为 1,然后看看这个二进制是多少,就可以了。(前提,根据瓶子的编号,分配老鼠吃药,二进制位上为 1 表示吃。)

    方法是不是唯一的呢?对称的还有一种,根据瓶子的编号,还是给老鼠喂药,对应编号为 1 的不喂,对应编号为 0 的反而喂药,最终判断方法如何呢?

    将第 i 只老鼠死了,记二进制的第 i 位为 0,然后看看这个二进制是多少,就可以了。(前提,根据瓶子的编号,分配老鼠吃药,二进制位上为 1 表示不吃。)

  10. 在 C 语言中下面那个语句的结果是 1 ?( 5 分 )
    A. main 函数正常结束的返回值
    B. return 7&1;
    C. char *p="hello"; return p == "hello";
    D. return "hello" == "hello";
    E. 上面都不对

    分析:A 一定是错的,C/C++ main 函数正常结束,返回 0;B 一定是对的,C 和 D 呢?标准 C 规定是,同样两个字符串字面量,比如 "hello",内存是放一份还是两份,由编译器决定!所以,C 和 D 的结果,一般的编译器应该会有警告提示,说明是 undefined behaviour !(前面第 2 题和第 5 题,也有遇到过!) 所以,C 和 D 只能说很可能是对的,但是却不一定!

  11. F、G、X 都是32位有符号整数,F=X/2,G=X>>1,如果 F != G,那么:( 5 分 )
    A. 编译错误
    B. X 是奇数
    C. X 是负数
    D. F-G=1
    E. G-F=1

    分析:前面第 5 题和第 10 题都出现了 undefined behavior 的行为!不幸的是,这里又出现了一个!~~~~(>_<)~~~~ 这是 Microsoft 的错,这种题就根本就不应该出!

    A 肯定是错的,C 肯定是对的,因为正数和0,一定不会出现 /2 和 >>1 不相等的情况。然后 B 和 D 呢?如同上一题,几乎可以肯定滴说,B 和 D 也是对的,为神马是几乎肯定?因为负数的右移,多数都是实现为“算术移位”,如果是“逻辑移位”的话,一个负数移位之后变成了一个正数,B 和 D 都不对。
    现在说,“算术移位”,B 是对的。算术移位,最高位填 1,对于负偶数来说,/2 和 >>1 结果还是一样,跟正数类似,所以,结果不一样,一定是奇数!其次,其次!!欲哭无泪丫……5/2 == 2,5%2 == 1,这个结果是绝对的!但是对于 -5/2 == ? -5%2 == ? 上,标准是没有规定的,于是又出现一个 undefined behaviour! 一般的实现是,模运算的结果要和被除数一致,然后保证 (a/b)*b + (a%b) == a,所以-5%2 == -1,-5/2 == -2,同时 -5 >> 1 == -3,于是乎 F-G = 1。me 死的心都有了,O__O"…

  12. 3*4 的方格,有多少个长方形?( 5 分 )
    A. 18
    B. 20
    C. 40
    D. 60
    E. 上面都不对

    分析:高中数学题目,O__O"…(本来想解释一下的,但是赶脚么必要丫,公式 C(2,4) * C(2,5) = 6*10 = 60,横向取 2 条线,纵向取 2 条线,就是一个长方形。)

  13. 一个直线将一个平面分成 2 部分,两条直线分成 4 部分,如果直线不平行,多条直线不共一点,问 100 条直线将平面分成几部分?( 5 分 )
    A. 5051
    B. 5053
    C. 5510
    D. 5511

    分析:小学数学题目,O__O"…(公式:1+1+2+3+4+...+n=1+(n+1)n/2 = 1+5050 = 5051)

  14. 下面哪些是稳定排序:( 5 分 )
    A. 冒泡儿排序
    B. 快速排序
    C. 堆排序
    D. 归并排序
    E. 选择排序

    分析:略。

  15. Web 应用程序中常使用 MVC 模式,关于说法下面哪些是对的?( 5 分 )
    A. 模型 ( Model )表示数据以及处理数据的业务逻辑
    B. 视图 ( View ) 是对模型的(可视化)展示,它渲染模型的结果,典型的是一个用户接口元素(user interface element)
    C. 控制器介于用户和系统之间,它接受用户的输入,指挥着模型和视图来完成输入对应的任务

    D. MVC 的常用实践是,模型从用户接收 GET 和 POST 的请求,然后决定做神马,通过移交给控制器和视图
    E. 上面都不对

    分析:MVC 貌似就是 ABC 的说法。

  16. 根据下面哪些可以确定一棵二叉树?( 5 分 )
    A. 前序遍历和中序遍历
    B. 前序遍历和后序遍历
    C. 中序遍历和后序遍历
    D. 后序遍历

    分析:至少要一个中序遍历,前序+后序遍历不中。

  17. n 个字符构成的字符串,假设每个字符都不一样,问有多少个子串?( 5 分 )
    A. n+1
    B. n^2
    C. n(n+1)/2
    D. 2^n-1
    E. n!

    分析:介个,貌似也简单,长度为 1 的字符串 n 个,长度为 2 的 n-1 个,长度为 3 的 n-2 个,...,长度为 n 的 1 个,然后 n+(n-1)+(n-2)+...+1 = ?。

  18. 根据下面给的表和 SQL 语句,问执行 SQL 语句更新多少条数据?( 5 分 )
    sql 语句:
        update Books set NumberOfCopies = NumberOfCopies + 1 where AuthorID in
        select AuthorID from Books
        group by AuthorID
        having sum(NumberOfCopies) <= 8 
    
    表中数据:
    BookID        Tittle              Category               NumberOfCopies             AuthorID
    1           SQL Server 2008         MS                            3                    1
    2           SharePoint 2007         MS                            2                    2
    3           SharePoint 2010         MS                            4                    2
    5           DB2                     IBM                           10                   3
    7           SQL Server 2012         MS                            6                    1
    

    A. 1 B. 2 C. 3 D. 4 E. 5

    分析:不多说,SQL 语句执行。

  19. 下图中从 S 到 T 的最短路径是多少?(边上是连接点的长度):( 13 分 )



    A. 17
    B. 18
    C. 19
    D. 20
    E. 21

    分析:从前向后找最短路径,先是 A1、A2、A3,然后 B1、B2,其次 C1、C2,最后 T 。

  20. N个球中有一个假冒伪劣(重量不足),如果给你一个天平允许你测 3 次找出那个假冒伪劣,问 N 可能的值?( 13 分 )
    A. 12
    B. 16.
    C. 20
    D. 24

    E. 28

    分析:3 个一次可以测出来,3*3 = 9 个以内 2 次,3*3*3 = 27 个以内,3次!所以,有个公式:n 次可以测出来 3^n 以内的假冒伪劣,至于怎么测,方法都一样,平均分成三墩,然后,略。

题目简单分析:

  1. C 语言 3 道:5、10、11:++ 运算符、字符串指针、按位运算;(各种 bug 程序!)
  2. C++ 语言 3 道:2、7、8:多态、const 关键字、const 和指针、POD;
  3. Java/C# 语言 1 道:6:多维数组;
  4. 数据结构 5 道:3、14、16、17、19:链表和数组、稳定排序、二叉树、字符串、最短路径;
  5. Windows 程序设计 1 道:1:函数修饰符;
  6. 操作系统 1 道:4:线程和进程;
  7. 设计模式 1 道:15:MVC 模式;
  8. 数据库 1 道:18:sql 语句;
  9. 数学 2 道:12、13:组合数学;
  10. 智力题 2 道:9、20:二进制、等比数列;

分值估计

下面是 me 的分值估计,最佳估计,就是将那些比较肯定的暂时米有发现有问题的,就看成是“完全正确”;大致估计,将有些拿不准的但是现在没有发现错误的,化作“半对”;最坏估计,将某些可能错的(和其他人想法有出入),就化为“错误”,但是比较肯定的还是算做“完全正确”。分值列出来,原来自己的水平就是那样丫!希望尽可能滴接近实际情况:

最佳估计:0 + 3 + 3 + 2 - 2 + 3 + 3 + 0 - 3 + 5 + 3 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 13 + 7 = 72
大致估计:0 + 3 + 2 + 2 - 2 + 3 + 3 + 0 - 3 + 3 + 3 + 5 + 5 + 5 + 3 + 5 + 5 + 5 + 13 + 7 = 67
最差估计:0 + 3 - 2 + 2 - 2 + 3 + 3 + 0 - 3 + 3 - 3 + 5 + 5 + 5 - 3 + 5 + 5 + 5 + 13 + 7 = 51

你可能感兴趣的:(job,c,c++)