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++; //证明此处为边界,周长++
}