Google面试受挫,听说以后找实习找工作都会考这样的题目,决定在百忙中一天至少写一道题,题目从poj百练上找。
Edge Detection是一道模拟类题目,但是如果逐个pixel进行计算,妥妥的TLE+MLE,思考半天无果,看了这篇博客以后明晰了思路,http://leons.im/posts/poj-1009-edge-detection-report/。
我们解决这道题的本质是寻找转换后的图片中,每一串value中的第一个值的位置,我们把这个点称为输出起始点,找到所有的可能的输出起始点就能得到输出的结果。
在上面的博客中,证明的一个核心定理在于,一个输出起始点的周围八个点中,一定至少有一个输入起始点。那么反过来,根据所有的输入起始点,可以找到所有可能的输出起始点,把这些所有的输出起始点找出来,处理一下就能得到输出结果。
具体的证明过程上面的博客写的很好,我这里再说一下细节上的问题:
1、要把height*width这个位置的点(也就是最后一个点的下一行的第一个点)作为输入起始点,不然的话左下角的这个点不符合上述定理;
2、在计算某个点和周围的点的最大距离的时候,考虑这个点的位置,即左边界还是右边界还是中间,而上下边界,反正在get_value()的时候也找不出相应的值,所以上下边界的情况其实不用考虑。
#include <iostream>
#include <algorithm>
#include <memory.h>
#include <cmath>
using namespace std;
#define MAX_PAIRS 1005
//用于记录有可能成为处理过后的图片的起始点的点
struct output_point{
public:
int value;
int pos;
bool operator< (output_point b){
if(this->pos < b.pos)
return true;
return false;
}
};
output_point points[MAX_PAIRS*8];
int output_count = 0;
int width = 0;
//用于记录输入数据
int pairs[MAX_PAIRS][2] = {0};
//用于记录输出的结果
int ans[MAX_PAIRS][2] = {0};
//给定一个pixel在所有pixel中的位置,返回这个pixel对应的值,如果越出图片外,则返回-1
int get_value(int pos, int pair_count){
if(pos <= 0){
return -1;
}
for(int i=0;i<pair_count;i++){
if(pos <= pairs[i][1]){
return pairs[i][0];
}
else{
pos -= pairs[i][1];
}
}
return -1;
}
//给定一个pixel的中心点,计算这个点和周围的点的最大距离,并且记录到points数组中,成为待定的起始点
void cal_dis(int pos, int pair_count,int width){
int center = get_value(pos, pair_count);
if(center == -1)
return;
//分成四种情况考虑,考虑这个点是在图片的左边缘,右边缘还是中间,另外考虑了width=1这种同时接触左右边缘的情况
int p[4][8] = {{0,0,0,-width,width,1,1-width,1+width},
{-1,-1+width,-1-width,-width,width,1,1+width,1-width},
{-1,-1+width,-1-width,-width,width,0,0,0},
{0,0,0,-width,width,0,0,0}};
int case_num = -1;
if(width == 1)
case_num = 3;
else if(pos%width == 1)
case_num = 0;
else if(pos%width == 0)
case_num = 2;
else
case_num = 1;
//计算和周围点的距离的最大值
int max_dis = 0;
for(int i=0;i<8;++i){
int t_pos = pos + p[case_num][i];
int t_dist = get_value(t_pos,pair_count);
if(t_dist != -1){
t_dist = abs(t_dist - center);
if(t_dist > max_dis)
max_dis = t_dist;
}
}
points[output_count].pos = pos;
points[output_count].value = max_dis;
output_count ++;
return;
}
//给定一个输入数据的起始点,这个点周围的八个点都有可能成为输出数据的起始点
void cal_point(int pos, int pair_count,int width){
int p[9] = {-1,-1-width,-1+width,-width,width,1,1-width,1+width,0};
for(int i=0;i<9;++i){
cal_dis(pos+p[i], pair_count,width);
}
return;
}
int main(){
while(true){
scanf("%d",&width);
if(width == 0)
break;
memset(pairs,0,sizeof(pairs));
memset(points,0,sizeof(points));
memset(ans,0,sizeof(ans));
output_count = 0;
int pair_count = 0;
int value, length;
while(true){
scanf("%d%d",&value,&length);
if(value == 0 && length == 0)
break;
pairs[pair_count][0] = value;
pairs[pair_count][1] = length;
pair_count += 1;
}
//找到所有的输入数据的起始点坐标
int current = 0;
for(int i=0;i<pair_count;++i){
int start_point = current + 1;
cal_point(start_point,pair_count,width);
current += pairs[i][1];
}
cal_point(current + 1,pair_count,width);
sort(points,points + output_count);
//根据所得到的起始点坐标,得到输出结果
int ans_count = 0;
for(int i=0;i<output_count;++i){
if(i==0){
ans[ans_count][0] = points[0].value;
ans[ans_count][1] = points[0].pos;
ans_count ++;
continue;
}
if(points[i].value != ans[ans_count-1][0]){
ans[ans_count-1][1] = points[i].pos - ans[ans_count-1][1];
ans[ans_count][0] = points[i].value;
ans[ans_count][1] = points[i].pos;
ans_count++;
}
}
ans[ans_count-1][1] = current + 1 -ans[ans_count-1][1];
printf("%d\n",width);
for(int i=0;i<ans_count;++i){
printf("%d %d\n",ans[i][0],ans[i][1]);
}
printf("0 0\n");
}
printf("0\n");
return 0;
}