【NOJ1145】【算法实验二】【回溯算法】求图像的周长


1145.求图像的周长

时限:1000ms 内存限制:10000K  总时限:3000ms

描述

给一个用 ‘ . ’ 和 ' X ' 表示的图形,图形在上、下、左、右、左上、左下、右上、右下8个方向都被看作是连通的,并且图像中间不会出现空洞,求这个图形的边长。

输入

首先给出m、n、x、y四个正整数,下面给出m×n的图形,x、y表示点击的位置,全0表示结束。

输出

点击的图形的周长。

输入样例(此处为了排版好看使用了空格)

2 2 2 2

XX

XX

6 4 2 3

. XXX

. XXX

. XXX

. . . X

. . X .

X . . .

0 0 0 0

输出样例

8

18

提示

参考迷宫问题,实现时关键要解决好各块的表示问题。


#include <iostream>

using namespace std;

char photo[1000][1000]; //表示图像,左上角为[0,0]
int used[1000][1000];   //标记方格是否被扩展,1==已扩展
int m,n,x,y;
int l;  //周长

void dfs(int k);

//判断方格[x,y]能否向方向d扩展
bool canmoveto(int x,int y,int d);

//返回方格s的序号
int moveto(int x,int y,int d);

int main()
{
    int i,j;
    cin>>m>>n>>x>>y;
    while(!(m==0&&n==0&&x==0&&y==0)){
        //输入数据并初始化
        for(i=0;i<m;i++){
            for(j=0;j<n;j++){
                cin>>photo[i][j];
                used[i][j]=0;   //初始化
            }
        }

        l=0;    //初始化

        dfs((x-1)*n+(y-1)); //初始值为点击位置的序号

        cout<<l<<endl;

        cin>>m>>n>>x>>y;
    }
    return 0;
}

void dfs(int k)     //从方格k出发向其他方向扩展(深搜)
{
    int x=k/n,y=k%n;    //计算方格k的行列
    used[x][y]=1;       //标记本方格已被扩展

    int d;  //扩展方向
            //约定1-4分别代表上、下、左、右、
            //约定5-8分别代表左上、左下、右上、右下
    for(d=1;d<=8;d++){
        if(canmoveto(x,y,d)){   //若能够向方向d扩展
            int s=moveto(x,y,d);    //求将要扩展的方格序号s
            if(!used[s/n][s%n])     //若该方格未被扩展
                dfs(s);             //从方格s出发继续扩展
        }
        else if(d<=4){          //若不能够向上下左右扩展
            l++;                //证明此处为边界,周长++
        }
    }
}

//函数功能:判断方格[x,y]能否向方向d扩展
//判断条件:此方格在方向d上有邻居格子且为'X'
//注:小心数组越界
bool canmoveto(int x,int y,int d)
{
    switch(d)
    {
        case 1: //上
        {
            if(x>0&&photo[x-1][y]=='X')
                return true;
            else break;
        }
        case 2: //下
        {
            if(x<m-1&&photo[x+1][y]=='X')
                return true;
            else break;
        }
        case 3: //左
        {
            if(y>0&&photo[x][y-1]=='X')
                return true;
            else break;
        }
        case 4: //右
        {
            if(y<n-1&&photo[x][y+1]=='X')
                return true;
            else break;
        }
        case 5: //左上
        {
            if(x>0&&y>0&&photo[x-1][y-1]=='X')
                return true;
            else break;
        }
        case 6: //左下
        {
            if(x<m-1&&y>0&&photo[x+1][y-1]=='X')
                return true;
            else break;
        }
        case 7: //右上
        {
            if(x>0&&y<n-1&&photo[x-1][y+1]=='X')
                return true;
            else break;
        }
        case 8: //右下
        {
            if(x<m-1&&y<n-1&&photo[x+1][y+1]=='X')
                return true;
            else break;
        }
    }
    return false;
}

//函数功能:返回方格s的序号
int moveto(int x,int y,int d)
{
    switch(d)
    {
        case 1:return ((x-1)*n+(y));    //上
        case 2:return ((x+1)*n+(y));    //下
        case 3:return ((x)*n+(y-1));    //左
        case 4:return ((x)*n+(y+1));    //右
        case 5:return ((x-1)*n+(y-1));  //左上
        case 6:return ((x+1)*n+(y-1));  //左下
        case 7:return ((x-1)*n+(y+1));  //右上
        case 8:return ((x+1)*n+(y+1));  //右下
    }
    return 1;   //无实际意义,为消warning
}11

【后记】

1.这道题和上一道题【农场灌溉问题】,在昨晚把我虐哭,然而今天再看,突然发现提示真的很有用,本质上就是最简单的迷宫深搜问题,最难的反而是数据表示和访问次数的判断

2.在下面这段代码中有一个需要注意的问题,最开始我写判断canmoveto函数的时候,把“该格已访问过”也加入了返回false的条件里,然而这是不对的,会造成错误的周长++;

        if(canmoveto(x,y,d)){   //若能够向方向d扩展
            int s=moveto(x,y,d);    //求将要扩展的方格序号s
            if(!used[s/n][s%n])     //若该方格未被扩展
                dfs(s);             //从方格s出发继续扩展
        }
        else if(d<=4){          //若不能够向上下左右扩展
            l++;                //证明此处为边界,周长++
        }

猜你喜欢

转载自blog.csdn.net/qq_41727666/article/details/82913241