C语言程序设计100例之(63):红与黑

例63   红与黑

问题描述

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

输入

包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下

1)‘.’:黑色的瓷砖;

2)‘#’:白色的瓷砖;

3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。

当在一行中读入的是两个零时,表示输入结束。

输出

对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

输入样例

6 9

....#.

.....#

......

......

......

......

......

#@...#

.#..#.

0 0

输出样例

45

        (1)编程思路。

        设函数f(x, y)计算从点(x,y)出发能够走过的黑瓷砖总数,则

        f(x, y) = 1 + f(x - 1, y) + f(x + 1, y) + f(x, y - 1) + f(x, y + 1)

        若点(x,y)超过了房子的范围(x < 0 || x >= w || y < 0 || y >= h)或该处为白色瓷砖,则f(x, y)的返回值为0。

        编写简单的递归函数即可。在函数中凡是走过的黑色瓷砖不能够被重复走过。可以通过每走过一块瓷砖就将它作标记的方法(最简单的方法是将其标记为白色瓷砖)保证不重复计算任何黑色瓷砖。

(2)源程序。

#include <stdio.h>

int w,h;

char map[21][21];

int f(int x, int y)

{

    if(x < 0 || x >= w || y < 0 || y >= h)   // 如果走出房子范围

       return 0;

    if(map[x][y] == '#')

       return 0;

    map[x][y] = '#';                 // 将走过的瓷砖做标记

    return 1 + f(x-1, y)+f(x+1,y)+f(x,y-1)+f(x,y+1);

}

int main()

{

    int i, j;

    while(scanf("%d %d", &h, &w) && w!=0 && h!=0)

    {

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

          scanf("%s", map[i]);

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

          for(j = 0; j < h; j++)

             if (map[i][j]=='@') printf("%d\n", f(i,j));

    }

    return 0;

}

习题63

63-1 马走日

问题描述

马在中国象棋以日字形规则移动。

请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。

输入

第一行为整数T(T < 10),表示测试数据组数。

每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标n,m,x,y。(0<=x<=n-1,0<=y<=m-1, m < 10, n < 10)

输出

每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,0为无法遍历一次。

输入样例

1

5 4 0 0

输出样例

32

        (1)编程思路。

        马走日可以从一个位置跳到其他的8个位置,编写函数void dfs(int x,int y,int step),三个参数的含义是马第step步走到了位置(x,y)处。

        在函数中,若马走到的8个位置之一(tx,ty)在棋盘中并且没有走过(vis[tx][ty]为初始值0),则马走到该位置,即递归调用dfs(tx,ty,step+1)。

        若step==n*m,表示棋盘所有的位置都走过,计数cnt++。cnt定义为全局变量,初始值设为0。

       (2)源程序。

#include <stdio.h>

#include <string.h>

int cnt=0;

int vis[11][11]={0};

int n,m;

int dx[8]={-1,1,-2,2,-2,2,-1,1},dy[8]={-2,-2,-1,-1,1,1,2,2};

void dfs(int x,int y,int step)

{

    if (step==n*m)

    {

        cnt++;

        return;

    }

    int i;

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

    {

        int tx=x+dx[i];

        int ty=y+dy[i];

        if (tx<0 || tx>=n || ty<0 || ty>=m || vis[tx][ty]==1)

            continue;

        vis[tx][ty]=1;

        dfs(tx,ty,step+1);

        vis[tx][ty]=0;

    }

}

int main()

{

    int t;

    scanf("%d",&t);

    while (t--)

    {

        memset(vis,0,sizeof(vis));

        cnt=0;

        int x,y;

        scanf("%d%d%d%d",&n,&m,&x,&y);

        vis[x][y]=1;

        dfs(x,y,1);

        printf("%d\n",cnt);

    }

    return 0;

}

63-2  城堡问题

问题描述

 C语言程序设计100例之(63):红与黑

图1是一个城堡的地形图,其中符号“#”表示墙,“|”和“-”表示没有墙。

请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分割成m×n(m≤50,n≤50)个方块,每个方块可以有0~4面墙。

输入

输入第一行是两个整数n和m,分别是南北向、东西向的方块数。在接下来的n行里,每行有m个整数,一个整数表示一个方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。例如,方块(1,1)的数字11表示该方块周围有1堵西墙,1堵北墙和1堵南墙,即1+2+8=11。城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。输入的数据保证城堡至少有两个房间。

输出

城堡的房间数、城堡中最大房间所包括的方块数。

输入样例

4 7

11 6 11 6 3 10 6

7 9 6 13 5 15 5

1 10 12 7 13 7 5

13 11 10 8 10 12 13

输出样例

5

9

        (1)编程思路。

        表示方块周围墙的数字对应二进制数的低4位,每位表示一堵墙。最低位为1(设为第0位),表示西边有墙;第1位为1,表示北边有墙;第2位为1,表示东边有墙;第3位为1,表示南边有墙。可以简单用该数字分别与1、2、4、8进行按位与运算,若运算结果为0,则表示对应位置没有墙。某方向没有墙就可以继续向该方向走,同时方块数加1。

        编写简单的递归函数void dfs(int i,int j)求解以位置(i,j)出发的连通块中方块的个数。

      (2)源程序。

#include <stdio.h>

int map[51][51],vis[51][51]={0};

int area;

void dfs(int i,int j)

{

       if (vis[i][j]) return ;

       vis[i][j]=1;

       area++;

       if ((map[i][j]&1)==0) dfs(i,j-1);   // 西边没有墙,向西走

       if ((map[i][j]&2)==0) dfs(i-1,j);   // 北边没有墙,向北走

       if ((map[i][j]&4)==0) dfs(i,j+1);  // 东边没有墙,向东走

       if ((map[i][j]&8)==0) dfs(i+1,j);  // 南边没有墙,向南走

}

int main()

{

        int n,m;

       scanf("%d%d",&n,&m);

       int i,j;

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

         for (j=1;j<=m;j++)

            scanf("%d",&map[i][j]);

       int sum=0,maxArea=0;

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

         for (j=1;j<=m;j++)

         {

              if(!vis[i][j])

              {

                     sum++;

                     area=0;

                     dfs(i,j);

                     if (maxArea<area) maxArea=area;

              }

         }

       printf("%d\n%d\n",sum,maxArea);

       return 0;

}

63-3  棋盘问题

问题描述

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

输入

输入含有多组测试数据。

每组数据的第一行是两个正整数,n k(n <= 8 ,k <= n),用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。

当为-1 -1时表示输入结束。

随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

输出

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

输入样例

2 1

#.

.#

4 4

...#

..#.

.#..

#...

-1 -1

输出样例

2

1

         (1)编程思路。

        编写递归函数void DFS(int row,int num)进行逐行搜索,row为当前搜索行,num为已填充的棋子数。

        定义数组int vis[10]用于标某一列上是否有棋子,因为每次递归下一行,所以每一行不会有冲突,只需判断这一列上是否有其他棋子。

        在第row行放棋子时,在该行找到一列j,如果row行j列没被标记放过棋子(vis[j]==0)且位置在棋盘内(map[i][j]=='#'),那么在该列放上棋子,并标记,即vis[j]=1;之后递归搜索下一行放下一个棋子,调用函数DFS(row+1,num+1)。

        若棋子已经用完(num>=k),则记录方案数加1,然后直接返回。

       (2)源程序1。

#include<stdio.h>

int vis[10];

char map[10][10];

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

void DFS(int row,int num) 

{

    if(num>=k)

    {

        ans++;

        return;

    }

    int i,j;

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

    {

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

        {

            if (!vis[j] && map[i][j]=='#')

            {

                vis[j]=1;  

                DFS(i+1,num+1);

                vis[j]=0; 

            }

        }

    }

}

int main()

{

       int i;

    while (scanf("%d%d",&n,&k))

    {

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

            break;

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

                     vis[i]=0;

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

            scanf("%s",map[i]);

        ans=0;

        DFS(0,0);

        printf("%d\n",ans);

    }

    return 0;

}

        实际上,每行在放棋子的过程中,要么在该行找一个可行的列放一个棋子,要么在该行不放棋子。按这一思路,可以编写如下的程序。

       (3)源程序2。

#include<stdio.h>

int vis[10];

char map[10][10];

int ans,k,n;

void DFS(int row,int num)

{

    if(num==k)

    {

        ans++;

        return;

    }

    if (row>=n) return ;

    int j;

    for (j=0;j<n;j++)   // 当前行放一个棋子

    {

         if(!vis[j] && map[row][j]=='#')

         {

             vis[j]=1;

             DFS(row+1,num+1);

             vis[j]=0;

         }

    }

    DFS(row+1,num);      // 当前行不放棋子

}

int main()

{

    int i;

    while (scanf("%d%d",&n,&k))

    {

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

            break;

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

                     vis[i]=0;

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

            scanf("%s",map[i]);

        ans=0;

        DFS(0,0);

        printf("%d\n",ans);

    }

    return 0;

}

上一篇:Java集合- HashMap 的7种遍历方式


下一篇:试题 算法训练 P0705