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;
}