POJ1009

来源:互联网 发布:java趣味编程题 编辑:程序博客网 时间:2024/06/05 20:54

转载自:POJ1009---------无奈了

想刷一点水题,就随便看了看,发现1009,30%。嗯于是就它了。结果这道题,无奈了我很久。

题目:http://poj.org/problem?id=1009

首先,暴力必挂,这是题目的善意提醒。

于是,一直在想不暴力的各种判断计算方法,关于各种跳跃移动,后来都无奈想用STL。原谅我的蒟蒻。

再然后就思维混乱了。于是看网上各位大神的解题报告。很神奇的一个搞法。瞬间被震惊。发现了一个道理,有时候从这个角度搞不通的时候,换一个更直接的角度往往一下就搞通了。是的。

首先可以看出来,这个最终的答案中,肯定会有某些连续段,答案是不变的,这些连续段又和原图有关。我自己的思路就是,先找出在原图的某一连续段中,答案值可能改变的几个像素(做标记),再一次次跳跃到这几个像素中计算它们的答案值,但是问题就是,怎么样的像素才是需要计算的像素。

如图的一个map:



第一个x显然是需要标记的。然后,可以直接跳跃到第4个x,因为此时它的周围多了一个z,答案可能改变,而第二、三个x显然答案和第一个是一样的。再然后,y的第一个也肯定要标记,因为主体变了。然后到了坐标为(2,3)的y,它需要标记是因为它的周围多了一个m。于是以此类推完成。虽然这个搞法看似可以,但这么多的细节要考虑,况且格子数是10^9,以我的水平注定挂。该怎么办呢。在看了各位大神的题解之后,发现其实我只要把需要标记的格子标记出来,并且把每一连续段的分段画出来,就可以发现很神奇的东西:


紫色标注的都是要标记的格子,红色边框的代表这一个连续段的起始格。我们可以发现,每个连续段的起始格,都是要标记的格子,同时,每个要标记的格子,都是一个连续段起始格的周围8个格子中的一个。所以,改进的搞法就很清楚了:只需要一个个枚举每个连续段的起始格,并计算它和它四周8个格子的答案值,最后统计答案的时候按照位置先后排序,答案中相同的连续段就合并。因为最多只有1000个连续段,所以不管是时间还是空间都不会超。

有些具体的东西程序里注释:

#include<cstdio>#include<cstring>#include<algorithm>#define size 1005using namespace std;struct pix{    int pos;    //表示答案中这个点的位置    int code;   //这个点上的答案值}outmap[size*8];int inmap[size][2];//inmap[][0]表示这个连续段的数值,inmap[][1]表示这个连续段的长度int width,cntp,tot;int cmp(pix x,pix y)//排序比较函数,最后以pos升序排序{    return x.pos<y.pos;}inline int abs(int x)//cmath库里其实有abs函数,这里无聊写了一个{    return x>0?x:-x;}int getnum(int pos)//返回原图中pos位置上的数值{    int p=0,i=0;    while (p<pos)        p+=inmap[i++][1];           return inmap[i-1][0];}int getcode(int pos)//计算pos位置上的答案{    int num=getnum(pos),ret=0;       int row=(pos-1)/width;//关于row和col的原理主程序中有    int col=(pos-1)%width;       for (int i=row-1;i<=row+1;i++)        for (int j=col-1;j<=col+1;j++)        {            int tpos=i*width+j;            if (i<0||j<0||j>=width||tpos>=tot || tpos==pos-1)                continue;//这里计算差的绝对值时要排除pos自己                           int tmp=getnum(tpos+1);            if (abs(tmp-num)>ret)ret=abs(tmp-num);//更新ret        }    return ret;}int main(){          while (scanf("%d",&width)&& width>0)    {                      int num,len;        cntp=tot=0;//必须得每次都赋0        while (scanf("%d%d",&num,&len)&& len>0)        {            inmap[cntp][0]=num;            inmap[cntp++][1]=len;            tot+=len;//tot是map中像素的个数        }        printf("%d\n",width);//按照同样格式输出                             int pos=1,k=0;//pos从1开始标号        for (int p=0;p<=cntp;p++)//枚举每一个连续段        {                          int row=(pos-1)/width;            int col=(pos-1)%width;                                  for (int i=row-1;i<=row+1;i++)                for (int j=col-1;j<=col+1;j++)                {                    int tpos=i*width+j;//这里算出来的tpos其实是tpos的标号减一                    if (i<0 || j<0 || j>=width || tpos>=tot)                        continue;//tpos在map的外面了                                           outmap[k].pos=tpos+1;                    outmap[k++].code=getcode(tpos+1);//答案存入outmap                   }                           pos+=inmap[p][1];//跳跃到下一个连续段的起始格        }                      sort(outmap,outmap+k,cmp);                      pix tmp=outmap[0];        for (int i=0;i<k;i++)        {            if (outmap[i].code==tmp.code) //表明连续,则跳过不输出                continue;            printf("%d %d\n",tmp.code,outmap[i].pos-tmp.pos);            tmp=outmap[i];        }        printf("%d %d\n",tmp.code,tot-tmp.pos+1);//最后一部分        printf("0 0\n");//按照格式输出    }    printf("0\n");//格式    return 0;}



0 0