【NOJ1145】【演算法實驗二】【回溯演算法】求影象的周長
阿新 • • 發佈:2018-12-12
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++; //證明此處為邊界,周長++ }