大家好,这是我的第一篇博文。
最近在做ACM练习,在讨论区发现有很多问题的解法多种多样,故萌生念头将自己已AC题目的解题思路整理发文。
发此博文不为求得最高效的解题代码,旨在分享自己的解题方法,与大家交流学习,共同探讨。
言归正传-----南阳ACM-27题 水池数目
水池数目
时间限制:3000 ms | 内存限制:65535 KB
难度:4
- 描述
- ***校园里有一些小河和一些湖泊,现在,我们把它们通一看成水池,假设有一张我们学校的某处的地图,这个地图上仅标识了此处是否是水池,现在,你的任务来了,请用计算机算出该地图中共有几个水池。
- 输入
- 第一行输入一个整数N,表示共有N组测试数据
每一组数据都是先输入该地图的行数m(0<m<100)与列数n(0<n<100),然后,输入接下来的m行每行输入n个数,表示此处有水还是没水(1表示此处是水池,0表示此处是地面) - 输出
- 输出该地图中水池的个数。
要注意,每个水池的旁边(上下左右四个位置)如果还是水池的话的话,它们可以看做是同一个水池。
- 样例输入
-
2 3 4 1 0 0 0 0 0 1 1 1 1 1 0 5 5 1 1 1 1 0 0 0 1 0 1 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1
- 样例输出
- 2
- 3
我的思路
在《数字图像处理》中,这类问题可用“区域生长法”来解决,有兴趣的可以查阅相关文献;
根据题目描述,四邻域有1相连通,可认为是一个区域(水池)。
这里利用数据结构中的堆栈来解决。
⑴首先将矩阵第一个为1(且未被标记)的点作为起始点,压入栈,并对其标记已入栈,水池数目+1;
⑵栈顶元素出栈,检查出栈元素周围四邻域[1]有无1(并且该位置没有被压入过栈[2]),若有则将其压入栈,并对其标记已入栈;
⑶返回(2),至栈底为空结束。
⑷返回(1),至循环矩阵结束。
[1]注意边界位置,四邻域不要导致数组下标越界。
[2]可建立一个临时标记场,标记入栈元素。
代码如下:
1 #include <iostream> 2 #include <string.h> 3 using namespace std; 4 5 typedef struct Point 6 { 7 int x; 8 int y; 9 int val; 10 }Point; 11 12 #define MAXSIZE 10000 //堆栈最大元素个数 13 #define OUTRANGE 0x0 //数组下标越界 14 //定义堆栈 15 template<class T> //模版类 16 class CStack 17 { 18 public: 19 CStack(int num); 20 int Push(T elem); //入栈 21 int Pop(T& elem); //出栈 22 int count(); //栈中元素个数 23 ~CStack(); 24 private: 25 int Maxnum; //栈元素最大数目 26 T *top; //栈顶指针 27 T *base; //栈底指针 28 }; 29 template <typename T> 30 CStack<T>::CStack(int num) 31 { 32 if(num>MAXSIZE) 33 //exit(0); //AC判题不允许 34 ; 35 Maxnum = num; 36 base = new T[Maxnum]; 37 top = base; 38 } 39 template <typename T> 40 CStack<T>::~CStack() //析构函数 41 { 42 top = NULL; 43 delete []top; 44 delete[]base; 45 base = NULL; 46 } 47 template <typename T> 48 int CStack<T>::Push(T elem) 49 { 50 if ((top-base)>=Maxnum) 51 { 52 return 0; 53 } 54 top++; 55 *top = elem; 56 return 1; 57 } 58 template<typename T> 59 int CStack<T>::Pop(T& elem) 60 { 61 if (top==base) 62 { 63 return 0; 64 } 65 elem = *top; 66 top--; 67 return 1; 68 } 69 template<typename T> 70 int CStack<T>::count() 71 { 72 return top - base; //等于 取整 (指针间地址长度/每个元素占用地址长度)-->本程序中为 (int)(top地址-base地址/sizeof(Point))=元素个数 73 } 74 int main() 75 { 76 int i_ceshi, iLine, iSample; 77 78 cin >> i_ceshi; 79 while (i_ceshi--) 80 { 81 int icount = 0;//水池个数 82 cin >> iLine >> iSample; 83 84 //构造水池矩阵 85 int **Matrix = new int*[iLine]; 86 for (int i = 0; i < iLine;i++) 87 { 88 Matrix[i] = new int[iSample]; 89 } 90 91 for (int i = 0; i < iLine;i++) 92 { 93 for (int j = 0; j < iSample;j++) 94 { 95 cin >> Matrix[i][j]; 96 } 97 } 98 99 //构造像素标记场 100 int *mark = new int[iLine*iSample]; 101 memset(mark, 0, iLine*iSample*sizeof(int)); //置为0 102 103 //运用堆栈,区域生长 104 CStack<Point> stack(iLine*iSample); 105 106 for (int i = 0; i < iLine;i++) 107 { 108 for (int j = 0; j < iSample;j++) 109 { 110 Point pxlpoint; //当前像元 111 pxlpoint.x = i; 112 pxlpoint.y = j; 113 pxlpoint.val= Matrix[i][j]; 114 int currentpx = i*iSample + j; 115 int mktmp = mark[currentpx]; //标记场像元 116 if (pxlpoint.val && mktmp == 0) { 117 stack.Push(pxlpoint); 118 mark[currentpx] = 1; 119 icount++; 120 } 121 122 while (stack.count()) 123 { 124 //pop 125 Point _pstack; 126 stack.Pop(_pstack); 127 Point _pLeft, _pRight, _pUp, _pDown; 128 129 /*----------------可修改为if else if语句------------------------------------------------------------*/ 130 _pLeft.x = _pstack.x; 131 _pLeft.y = _pstack.y> 0 ? _pstack.y -1 : _pstack.y; 132 133 _pRight.x = _pstack.x; 134 _pRight.y = _pstack.y< iSample - 1 ? _pstack.y+ 1 : _pstack.y; 135 136 _pUp.x = _pstack.x > 0 ? _pstack.x - 1 : _pstack.x; 137 _pUp.y = _pstack.y; 138 139 _pDown.x = _pstack.x < iLine - 1 ? _pstack.x + 1 : _pstack.x; 140 _pDown.y = _pstack.y; 141 142 _pLeft.val =_pstack.y > 0 ? Matrix[_pstack.x][_pstack.y - 1] : OUTRANGE; //上下左右四邻域 143 _pRight.val = _pstack.y < iSample - 1 ? Matrix[_pstack.x][_pstack.y + 1] : OUTRANGE; 144 _pUp.val = _pstack.x > 0 ? Matrix[_pstack.x - 1][_pstack.y] : OUTRANGE; 145 _pDown.val =_pstack.x < iLine - 1 ? Matrix[_pstack.x + 1][_pstack.y] : OUTRANGE; 146 /*---------------------------------------------------------------------------------------------------------*/ 147 //检查pop周围4邻域像元,若存在1且未标记,push,并标记 148 149 int MarkLeft = mark[_pLeft.x*iSample + _pLeft.y]; 150 int MarkRight = mark[_pRight.x*iSample + _pRight.y]; 151 int MarkUp = mark[_pUp.x*iSample + _pUp.y]; 152 int MarkDown = mark[_pDown.x*iSample + _pDown.y]; 153 154 if (_pLeft.val&&!MarkLeft){ 155 stack.Push(_pLeft); 156 mark[_pLeft.x*iSample +_pLeft.y] = 1; 157 } 158 if (_pUp.val&&!MarkUp){ 159 stack.Push(_pUp); 160 mark[_pUp.x*iSample + _pUp.y ] = 1; 161 } 162 if (_pRight.val&&!MarkRight) { 163 stack.Push(_pRight); 164 mark[_pRight.x *iSample + _pRight.y] = 1; 165 } 166 if (_pDown.val&&!MarkDown) { 167 stack.Push(_pDown); 168 mark[_pDown.x*iSample + _pDown.y] = 1; 169 } 170 171 }//while(stack.count()) 172 }//for 173 }//for 174 cout << icount << endl; 175 }//while(ceshi--) 176 return 0; 177 }