1. 程式人生 > >【NOJ1145】【演算法實驗二】【回溯演算法】求影象的周長

【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++;                //證明此處為邊界,周長++
        }