欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

DFS(三):八皇后问题

程序员文章站 2022-03-18 16:35:15
【例1】八皇后问题。 在一个8×8国际象棋盘上,放置8个皇后,每个皇后占一格,要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行、同一列或同一对角线上。问共有多少种不同的放置方法? (1)编程思路。 在八皇后问题中,由于任意两个皇后不同行,因此可以将布局表示为一维数组chess[8]。 ......

【例1】八皇后问题。

      在一个8×8国际象棋盘上,放置8个皇后,每个皇后占一格,要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行、同一列或同一对角线上。问共有多少种不同的放置方法?

      (1)编程思路。

      在八皇后问题中,由于任意两个皇后不同行,因此可以将布局表示为一维数组chess[8]。数组的下标i表示棋盘上的第i行,chess[i]的值表示皇后在第i行所放的位置。如chess[1]=5,表示在棋盘的第1行的第5列放一个皇后。

      为了寻找满足要求的布局chess,可依次产生部分布局(chess[0]),(chess[0]、chess[1]),…,直至最后产生出完整布局(chess[0]、chess[1]、…、chess[7])。每一步都要求保证它们是在不同列和不同对角线上。可采用深度优先搜索算法完成。

      (2)源程序。

#include <iostream>

using namespace std;

void show_chess(void);

int check(int n);

void putchess(int n);

int chess[8];

int main()

{

    cout<<"all results are :"<<endl;

    putchess(0);

    return 0;

}

// 递归函数:在从第n行开始放皇后

void putchess(int n)

{

    int i;

    if (n<8)

    {

      for (i = 0; i <8; i++)      // 将第n行从第一格(i)开始往下放

      {

         chess[n] = i;

         if (check(n) == 1)       // 若可放,则检查是否放满

         {

            if (n == 7)

               show_chess();     // 若已放满到8行时,则表示找出一种解,打印出来

            else

               putchess(n + 1);   // 若没放满则放下一行 putchess(n+1)

         }

     }

   }

}

// 根据前面几行的子,检查第n行所放的皇后是否合法

int check(int n)

{

    int i;

    for (i = 0; i <= n - 1; i++)

       if (chess[n] == chess[i] + (n - i) ||chess[n] == chess[i] - (n - i) ||chess[n] == chess[i] )

            return 0;

    return 1;

}

// 函数:打印结果

void show_chess(void)

{

    static int count = 0;

    cout<<"************* 第"<<++count<<"种 *************"<<endl;

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

    {

         for(int j=0; j<8; j++)

            if (j==chess[i]) cout<<"1 ";

                     else  cout<<"0 ";

         cout<<endl;

    } 

}


【例2】棋盘问题(poj 1321)。
description

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案c。
input

输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
output

对于每一组数据,给出一行输出,输出摆放的方案数目c (数据保证c<2^31)。
sample input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1
sample output

2
1

      (1)编程思路。

      在棋盘问题中,棋子摆放的位置只能是#, 且不能同行和同列。由于在深度优先搜索中采用按行递增的顺序来搜索的,这样每次递归下一行,所以每一行不会有冲突,不可能出现同行的情况。只需保证不同列,为判断某一列上是否有其他棋子,可定义一个数组visit[]来保存列的访问状态,visit[i]=true表示第i列上放置有棋子;visit[i]=false表示第i列上未放置棋子。

      (2)源程序。

#include <iostream>

using namespace std;

bool visit[20];

char map[20][20];

int ans,k,n;    // ans表示方案数,k表示棋子数目,n表示棋盘的大小

void dfs(int row,int num)   // 逐行搜索,row为当前搜索行,num为已填充的棋子数

{

    if(num>=k)   // 判断是否棋子已经放完,如果放完,记录方案数加1

    {

        ans++;

        return;

    }

    for(int i=row;i<n;i++)    

    {

        for(int j=0;j<n;j++)

        {

            if(!visit[j] && map[i][j]=='#')  // 如果该列没放棋子且该位置为棋盘,那么在这里放上棋子

            {

                visit[j]=true;             // 标记该列上有棋子

                dfs(i+1,num+1);   // 搜索下一行放下一个棋子

                visit[j]=false;           // 修改标记后递归回来要及时复原

            }

        }

    }

}

int main()

{

       int i;

      while (cin>>n>>k)

      {

          if (n==-1&&k==-1)

              break;

          for (i=0;i<n;i++)

             visit[i]=false;

          for (i=0;i<n;i++)

            cin>>map[i];

        ans=0;

        dfs(0,0);

        cout<<ans<<endl;

    }

    return 0;

}