面試例題1一個射擊運動員打靶,靶一共有10環(huán),連開10槍打中90環(huán)的可能性有多少種?請用遞歸算法編程實現(xiàn)。[中國某著名通信企業(yè)H面試題]

解析:靶上一共有10種可能——1環(huán)到10環(huán),還有可能脫靶,那就是0環(huán),加在一起共11種可能。這是一道考循環(huán)和遞歸的面試題。我們在這個程序中將利用遞歸的辦法實現(xiàn)打靶所有可能的演示,并計算出結(jié)果。讀者會問,難道一定要使用遞歸?當然不是。我們也可以連續(xù)用10個循環(huán)語句來表示程序,代碼如下:

for (i1=0;i1<=10;i1++)

 {

      for (i2=0;i2<=10;i2++)

      {

          for (i3=0;i3<=10;i3++)

          {

              ......

                for (i10=0;i10<=10;i10++)

                  {

                      if(i1+i2+i3+...+i10=90)

                      Print();

                 }

              ......

          }

      }

 }

但是,上面的循環(huán)程序雖然解決了問題,但時間復雜度和空間復雜度無疑是很高的。比較好的辦法當然是采用遞歸的方式,事實上公司也就是這么設計的。遞歸的條件由以下4步完成:

1)如果出現(xiàn)這種情況,即便后面每槍都打10環(huán)也無法打夠總環(huán)數(shù)90,在這種情況下就不用再打了,則退出遞歸。代碼如下:

if(score < 0 || score > (num+1)*10 ) //次數(shù)num09

         {

              return;

         }

2)如果滿足條件且打到最后一次(因為必須打10次),代碼如下:

    if(num == 0) 

     {

         store2[num] = score;

         Output( store2);

         return;

        

     }

3)如果沒有出現(xiàn)以上兩種情況則執(zhí)行遞歸,代碼如下:

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

        {

            //這里實際上為了方便把順序倒了過來,store2[9]是第1

            //store2[8]是第2回……store2[0]是第10

            store2[num] = i;

            Cumput(score - i, num - 1,store2);

        }

4)打印函數(shù),符合要求的則把它打印出來。代碼如下:

    public static void Output(int[] store2)

    {

       

        for(int i = 9; i>=0; --i)

        {

            Console.Write("   {0}",store2[i]);

           

        }

        Console.WriteLine();

        sum++;

               

    }

答案:

C#編寫的完整代碼如下:

using System ;

 

public class M

{

 

    //public static int[] store;

    //相當于設置了全局變量

    //這個全局變量sum是包含在M類中的

    public static int sum;

    public M()

    {

        int sum =0;

        // int[] store = {1,2,3,4,5,6,7,8,9,0};

       

    }

   

//打印函數(shù)

    //符合要求的則把它打印出來

    public static void Output(int[] store2)

    {

       

        for(int i = 9; i>=0; --i)

        {

            Console.Write("   {0}",store2[i]);

           

        }

        Console.WriteLine();

        sum++;

               

    }

   

    //計算總數(shù),返回sum

    public static int sum2()

    {

        return sum;

    }

 

    public static void Cumput(int score, int num, int[] store2 )

    {

       

        //如果總的成績超過了90環(huán)(也就是score<0),或者如果剩下要打靶

        //的成績大于10環(huán)乘以剩下要打的次數(shù),也就是說即便后面的都打10環(huán)

        //也無法打夠次數(shù),則退出遞歸

        if(score < 0 || score > (num+1)*10 ) //次數(shù)num09

        {

            return;

        }

       

        //如果滿足條件且達到最后一層

        if(num == 0) 

        {

            store2[num] = score;

            Output( store2);

            return;

           

        }

       

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

        {

            store2[num] = i;

            Cumput(score - i, num - 1,store2);

        }

        //Console.Write("   {0}",store2[5]);

    }

}

 

public class myApp

{

    public static void Main( )

    {

        int[] store;

        store = new int[10];

        int sum = 0;

        //int a=90;

        //int b=9;

        //Output();

        M.Cumput(90,9,store);

        sum = M.sum2();

       

        //M.Cumput2(a,b,store);

        //Console.Write("   {0}",store[3]);

        //cout<<"總數(shù):"<<sum<<endl;

        Console.Write(" 總數(shù):   {0}",sum);

       

    }

}  

程序結(jié)果一共有92 378種可能。

也可以用C++編寫,代碼如下:

#include <iostream>

using namespace std;

int sum;

int store[10];

void Output()

{

    for(int i = 9; i>=0; --i)

    {

       cout<<store[i]<<" ";

    }

   cout<<endl;

    ++sum;

}

 

void Cumput(int score, int num)

{

   if(score < 0 || score > (num+1)*10 ) //次數(shù)num09

      return;

   if(num == 0) 

     {

        store[num] = score;

        Output();

        return;

     }

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

     {

        store[num] = i;

        Cumput(score - i, num - 1);

     }

}

 

int main(int argc, char* argv[])

    {

       Cumput(90, 9);

       cout<<"總數(shù):"<<sum<<endl;

       return 0;

    }

面試例題2八皇后問題是一個古老而著名的問題,是回溯算法的典型例題。該問題是19世紀著名的數(shù)學家高斯1850年提出:在8×8格的國際象棋盤上擺放8個皇后,使其不能互相攻擊,即任意兩個皇后都不能處于同一行、同一列或同一斜線上,問有多少種擺法。[英國某著名計算機圖形圖像公司面試題]

解析:遞歸實現(xiàn)n皇后問題。

算法分析:

數(shù)組abc分別用來標記沖突,a數(shù)組代表列沖突,從a[0]~a[7]代表第0列到第7列。如果某列上已經(jīng)有皇后,則為1,否則為0

數(shù)組b代表主對角線沖突,為b[i-j+7],即從b[0]~b[14]。如果某條主對角線上已經(jīng)有皇后,則為1,否則為0

數(shù)組c代表從對角線沖突,為c[i+j],即從c[0]~c[14]。如果某條從對角線上已經(jīng)有皇后,則為1,否則為0

代碼如下:

#include <stdio.h>

 

static char Queen[8][8];

static int a[8];

static int b[15];

static int c[15];

static int iQueenNum=0; //記錄總的棋盤狀態(tài)數(shù)

 

void qu(int i);     //參數(shù)i代表行

 

int main()

{

 int iLine,iColumn;

 

 //棋盤初始化,空格為*,放置皇后的地方為@

 for(iLine=0;iLine<8;iLine++)

 {

    a[iLine]=0; //列標記初始化,表示無列沖突

    for(iColumn=0;iColumn<8;iColumn++)

      Queen[iLine][iColumn]='*';

 }

 

 //主、從對角線標記初始化,表示沒有沖突

 for(iLine=0;iLine<15;iLine++)

    b[iLine]=c[iLine]=0;

 

 qu(0);

 return 0;

}

 

void qu(int i)

{

 int iColumn;

 

 for(iColumn=0;iColumn<8;iColumn++)

 {

    if(a[iColumn]==0&&b[i-iColumn+7]==0&&c[i+iColumn]==0)

    //如果無沖突

    {

      Queen[i][iColumn]='@'; //放皇后

      a[iColumn]=1;           //標記,下一次該列上不能放皇后

      b[i-iColumn+7]=1;       //標記,下一次該主對角線上不能放皇后

      c[i+iColumn]=1;             //標記,下一次該從對角線上不能放皇后

      if(i<7) qu(i+1);        //如果行還沒有遍歷完,進入下一行

      else //否則輸出

      {

        //輸出棋盤狀態(tài)

        int iLine,iColumn;

        printf("%d種狀態(tài)為:\n",++iQueenNum);

        for(iLine=0;iLine<8;iLine++)

        {

          for(iColumn=0;iColumn<8;iColumn++)

            printf("%c ",Queen[iLine][iColumn]);

          printf("\n");

        }

        printf("\n\n");

      }

 

      //如果前次的皇后放置導致后面的放置無論如何都不能滿足要求,則回溯,重置

      Queen[i][iColumn]='*';

      a[iColumn]=0;

      b[i-iColumn+7]=0;

      c[i+iColumn]=0;

    }

 }

}