zoukankan      html  css  js  c++  java
  • 杂谈:编程解决水管工游戏

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

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

    以下内容改编自《啊哈,算法》第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 55
    
    int 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:
    ");
            for (i = 1; i <= top; i++)
                printf("(%d, %d) ", s[i].x, s[i].y);
            printf("
    ");
            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
    ");
        }
    
        return 0;
    }

    总结

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

  • 相关阅读:
    合并区间
    编译与运行
    传递信息
    划分字母区间
    无重叠区间
    用最少数量的箭引爆气球
    根据身高重建队列
    二叉树展开为链表
    动态添加按钮
    基础知识
  • 原文地址:https://www.cnblogs.com/wyf12138/p/6581532.html
Copyright © 2011-2022 走看看