POJ 1009 Edge Detection

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值