杂谈:编程解决水管工游戏

来源:互联网 发布:2016淘宝个性化刷法 编辑:程序博客网 时间:2024/04/29 18:38

杂谈:编程解决水管工问题

程序设计是一门极难上手的技能,仅仅凭着课堂上的知识,只能是熟悉一门编程语言的语法。但要是用计算机来解决一些实际的问题,哪怕是智力问题,课本上的知识是远远不够的。
编程就像学游泳。学游泳一定要在水里学,要在水里摸索体会。学编程也是如此。
下面给大家带来一个有趣的小问题,希望大家能够学习到其中的程序设计思维与方法,尤其是中间建立模型的过程,相当精彩,大家欣赏一下。

以下内容改编自《啊哈,算法》第4章第6节

问题描述:

一块矩形土地被分为N*M的单位正方形,这块土地里埋设一些水管,水管将从坐标为(1,1)的矩形土地的左上角左部边缘,延伸到坐标为(N,M)的矩形土地的右下角右部边缘。

水管只有两种:

  • 弯曲水管:L
  • 直水管: ━

土地中还有障碍物(比如树木等)

每种水管占据一个单位正方形土地。可以旋转这些管道,使其构成一个管道系统,创造一条从(1,1)到(N,M)的连通
管道。有障碍物的方格里没有管道。

建模开始:

我们用数字0表示障碍物,1到6表示管道的六种不同的摆放方式(如下表)。

┗ ┏ ┓ ┛ ━ ┃ 1 2 3 4 5 6

于是,程序的输入可以规定为:
第一行输入矩形土地的大小n,m。
接下来输入n行m列的数字,表示每个单位正方形土地中的管道情况(数字0表示障碍物,1到6表示管道)
样例输入为:
5 4
5 3 5 3
1 5 3 0
2 3 5 1
6 1 1 5
1 5 5 4
(自行脑补实际管道铺设情况)

程序要输出的是应该是铺设的路径,如果不存在这样的路径,则输出impossible。

样例输出:
The path is:
(1,1) (1,2) (2,2) (3,2) (3,3) (3,4) (4,4) (5,4)

(注:铺设管道的最左上角起点坐标为(1,1),最右下角终点坐标为(5,4),规定进水口在最左上角方格的左边,出水口在最右下角方格的右边,输出路径可能不唯一)

编程思路:

以下分析过程中的水管的图形与土地的状态请自行脑补

因为只有两种水管,直管(2种状态)和弯管(4种状态)。首先从(1,1) 开始尝试。(1,1)是直管,进水口又在
(1,1)的左边,因此(1,1)处的水管只能用5号摆放方式。

之后达到(1,2)。(1,2)处是弯管,进水口在(1,2)的左边,因此(1,2)有两种排放方式,分别是3号与4号。由于4号
摆放方式会出界,只能用3号摆放方式,从而来到了(2,2)。

(2,2)处是直管,进水口在上方,只能用6号摆放方式,接下来,来到(3,2)。

(3,2)是弯管,进水口在上面,有2种摆放方式可以选择,分别是1号和4号。
这两种选择都可以,我们就要分别去尝试……

依次类推,直到来到(n,m+1)为止,方案产生。

代码实现

这里用到了深度优先搜索DFS。当处在(x,y)处时,依次枚举当前管道的每一张摆放方式,但并非每一种都可以,还要
判断(x,y)处的进水口的方向。
这里规定进水口在左边用数字1表示,在上边用2表示,右边用3表示,下边用4表示。

要输出路径,只需要用一个栈存放相应的结点就可以了。

#include <stdio.h>#define MAX_N 55#define MAX_M 55int a[MAX_N][MAX_M], book[MAX_N][MAX_M];int n, m, flag = 0;struct node {    int x;    int y;}s[100];int top = 0;void dfs(int x, int y, int front)      //x,y表示当前处理的位置坐标,front表示(x,y)进水口的方法{    int i;    //判断是否达到终点    if (x == n && y == m + 1) {        flag = 1;                      //找到铺设方案        printf("The path is:\n");        for (i = 1; i <= top; i++)            printf("(%d, %d) ", s[i].x, s[i].y);        printf("\n");        return;    }    //判断是否越界    if (x < 1 || x > n || y < 1 || y > m)        return;    //判断(x,y)处是否已经遍历过    if (book[x][y] == 1)        return;    ++top; s[top].x = x; s[top].y = y;  //将当前坐标压栈    //当前水管是直管的情况    if (a[x][y] >= 5 && a[x][y] <= 6) {        if (front == 1) dfs(x, y+1, 1);  //进水口在左边,只能用5号摆放方式        if (front == 2) dfs(x+1, y, 2);  //进水口在上边,只能用6号摆放方式        if (front == 3) dfs(x, y-1, 3);  //进水口在右边,只能用5号摆放方式        if (front == 4) dfs(x-1, y, 4);  //进水口在下边,只能用6号摆放方式    }    //当前水管是弯管的情况    if (a[x][y] >= 1 && a[x][y] <= 4) {        //进水口在左边        if (front == 1) {            dfs(x+1, y, 2);            dfs(x-1, y, 4);        }        //进水口在上边        if (front == 2) {            dfs(x, y+1, 1);            dfs(x, y-1, 3);        }        //进水口在右边        if (front == 3) {            dfs(x-1, y, 4);            dfs(x+1, y, 2);        }        //进水口在下边        if (front == 4) {            dfs(x, y+1, 1);            dfs(x, y-1, 3);        }    }    book[x][y] = 0;   //取消标记    top--;            //将当前坐标从栈中弹出    return;}int main(void){    int i, j, num = 0;    while (scanf("%d %d", &n, &m) == 2) {        for (i = 1; i <= n; i++)            for (j = 1; j <= m; j++)                scanf("%d", &a[i][j]);        dfs(1, 1, 1);        if (flag == 0)            printf("impossible\n");    }    return 0;}

总结

学习编程的过程其实是一种修炼,不断挑战难题,挑战自己,才能不断提升自己的思维能力,锻炼自己的数理能力,
丰富自己的代码能力和编程技巧。
这里的水管工问题就是很有启发性的例子。还有许多有趣的问题值得我们用计算机编程去解决,这样我们可以从算法思维的角度考察问题,获得灵感。
这样的问题有很多,推荐大家一本书《算法趣题》,里面收录了很多有趣的问题,值得我们思考,并且在计算机上用代码实现。

0 0
原创粉丝点击