读完此题后,总的感觉就是没有具体的确定的优化方案,所以迟迟没法下手敲代码。于是在网上搜了一下解题报告,看了好几篇仍是感觉好凌乱。直到看了这一篇http://liangsun.org/posts/poj-1009-edge-detection-report/后,思路才算清晰起来。
引文首先给出了一个命题:每一个在输出结果中的像素点在原图像中的位置至少跟一个输入像素点相邻。
注:这里的相邻是指两个像素点的位置间距为-1+{0,-width,+width},0+{-width,+width}, 1+{0,-width,+width}。
这里面重复一下原著的证明,帮助自己加深一下理解。
证明采用的是反证法,即证明:一个像素点在原图像中所在的位置不跟任何输入像素点相邻,那么此像素点必定不会出现在输出结果中。
证:假如在图1中,x跟任何输入像素点都不相邻,那么b=a,c=b,e=x,g=h,h=f,并且由a,d,f也不是像素点可以得到图2,此时我们就看出红框的x跟其左边的x的最大差绝对值是一样的。所以红框的x必定不会在输出结果中。
如果x位于边界处,如在最左边,见图3,根据相邻点的定义,红框的x跟其左边的x的最大差绝对值也是一样的。
x位于最右边的情况为图4,红框的x仍然跟其左边的x的最大差绝对值是一样的。
定理的证明到现在基本完毕,但是有一种特殊情况没有考虑到,对于x处于最左边的情况,当处于图5的情况时,就有可能出现意外。
这个时候,红框的x与其左边的x的最大差绝对值有可能不一样了。原著是把height*width位置处也作为一个输入像素点。可以这样理解,对于原图像,如果还存在输入像素点的话,那么height*width肯定是下一个输入像素点。这样就不难理解height*width也作为一个像素输入点了。这样理解也就使得要证的命题是完全正确的。
注:如果无法理解height*width作为输入像素点的话,完全可以把红框的x作为一个特殊位置点,单独进行一下计算就行。至于红框x周围的点为啥不需要计算,自己简单推推就ok了。
原著的解题报告虽然是English版的,但是写的很通俗易懂,代码也很给力,读者可以参阅poj-1009-edge-detection-report
还是附上自己的代码:
#include <iostream>
#include <vector>
#include <map>
#include <cstdlib>
#include <cmath>
#include <utility>
#include <algorithm>
using namespace std;
int height, width;
vector<int> sum;
vector<pair<int,int> > imgI;
map<int, int> imgO;
int GetPixel(int pos)
{
for(int i = 0; i < imgI.size(); ++i)
if( pos < sum[i] )
return imgI[i].second;
}
void Cal(int pos)
{
if( imgO.find( pos ) != imgO.end() )
return ;
int row = pos/width, col = pos%width;
if( pos < 0 || pos >= sum.back() )
return ;
int pixel = GetPixel( pos );
int maxDiff = 0;
for(int i = row - 1; i <= row + 1; ++i)
for(int j = col - 1; j <= col + 1; ++j)
{
if( i < 0 || i >= height || j < 0 || j >= width)
continue;
int nPos = i*width + j;
//cout << row << " " << col << " -> " << nPos/width << " " << nPos%width << endl;
int nPixel = GetPixel( nPos );
maxDiff = max( maxDiff, abs(pixel-nPixel) );
}
imgO[pos] = maxDiff;
}
void Work()
{
for(int i = 0; i < imgI.size(); ++i)
{
int pos = imgI[i].first;
for(int k = -1; k <= 1; ++k)
for(int j = -1; j <= 1; ++j)
{
int nPos = pos + k*width + j;
Cal( nPos );
}
}
Cal( sum.back() - width );
}
void Print()
{
map<int,int>::iterator iter = imgO.begin();
int curPos = iter->first, curPixel = iter->second;
while( iter != imgO.end() )
{
if( iter->second != curPixel )
{
cout << curPixel << " " << (iter->first - curPos) << endl;
curPos = iter->first;
curPixel = iter->second;
}
++iter;
}
cout << curPixel << " " << (sum.back() - curPos) << endl;
cout << "0 0" << endl;
}
int main()
{
while( cin >> width && width )
{
cout << width << endl;
sum.clear();
imgI.clear();
imgO.clear();
int pixel, len;
int pos = 0;
while( cin >> pixel >> len )
{
if( !pixel && !len )
break;
imgI.push_back( make_pair(pos, pixel) );
pos += len;
sum.push_back( pos );
}
height = sum.back()/width;
Work();
Print();
}
cout << 0 << endl;
return 0;
}
GetPixel的二分查找实现为:
int GetPixel(int pos)
{
/*
for(int i = 0; i < imgI.size(); ++i)
if( pos < sum[i] )
return imgI[i].second;
*/
int low = 0, high = imgI.size() - 1;
while( low <= high )
{
int mid = (low + high)/2;
if( pos < sum[mid] )
high = mid - 1;
else low = mid + 1;
}
return imgI[low].second;
}