CTSC2000 冰原探险

来源:互联网 发布:jira软件下载 编辑:程序博客网 时间:2024/04/29 07:27

这是我第一次写博客,写的不好,请多见谅~

今天上午日常考试,第二题就是这个题目。
传送门
(其实我是在vijos上测评的)

题目描述 :

传说中,南极有一片广阔的冰原,在冰原下藏有史前文明的遗址。整个冰原被横竖划分成了很多个大小相等的方格。在这个冰原上有N个大小不等的矩形冰山,这些巨大的冰山有着和南极一样古老的历史,每个矩形冰山至少占据一个方格,且其必定完整地占据方格。冰山和冰山之间不会重叠,也不会有边或点相连。以下两种情况均是不可能出现的:
这里写图片描述
ACM探险队在经过多年准备之后决定在这个冰原上寻找遗址。根据他们掌握的资料,在这个冰原上一个大小为一格的深洞中,藏有一个由史前人类制作的开关。而唯一可以打开这个开关的是一个占据接近一格的可移动的小冰块。显然,在南极是不可能有这样小的独立冰块的,所以这块冰块也一定是史前文明的产物。他们在想办法把这个冰块推到洞里去,这样就可以打开一条通往冰原底部的通道,发掘史前文明的秘密。冰块的起始位置与深洞的位置均不和任何冰山相邻。这个冰原上的冰面和冰山都是完全光滑的,轻轻的推动冰块就可以使这个冰块向前滑行,直到撞到一座冰山就在它的边上停下来。冰块可以穿过冰面上所有没有冰山的区域,也可以从两座冰山之间穿过(见下图)。冰块只能沿网格方向推动。
这里写图片描述
请你帮助他们以最少的推动次数将冰块推入深洞中。
格式
输入格式
输入文件第一行为冰山的个数N (1<=N<=4000),第二行为冰块开始所在的方格坐标X1,Y1,第三行为深洞所在的方格坐标X2,Y2,以下N行每行有四个数,分别是每个冰山所占的格子左上角和右下角坐标Xi1,Yi1,Xi2,Yi2
输出格式
输出文件仅包含一个整数,为最少推动冰块的次数。如果无法将冰块推入深洞中,则输出0。
样例1
样例输入1[复制]
2
1 1
5 5
1 3 3 3
6 2 8 4
样例输出1[复制]
3
限制
各个测试点1s

解题过程:

当时自己也没多想,于是就敲了个bfs暴力,结果不幸的是while循环崩了,而且写的还不是正解。。于是。。结果你懂得
以下是我考试时的代码:

#include<cstdio>#include<cstdlib>int n,i,j,k,x1,y1,x2,y2,d,t1,t2,t3,t4,g=1;int f[3][1000001];bool b[2001][2001];int way[5][2]={0,0,-1,0,0,-1,0,1,1,0};void pd(int n){    if(f[0][n]==x2&&f[1][n]==y2)    {        printf("%d",d);        exit(0);    }}void bfs(int begin,int end){    int i,j;    for(i=begin;i<=end;i++) pd(i);    int x,y;    for(i=begin;i<=end;i++)      for(j=1;j<=4;j++)      {        if(f[2][i]+j==5) continue;        x=f[0][i];y=f[1][i];        while(1) {x+=way[j][0];y+=way[j][1];if(b[x][y]) break;}//这个while循环崩掉了//      while(!b[x+way[j][0]][y+way[j][1]]) {x+=way[j][0];y+=way[j][1];if(x==1999||y==1999) break;}        if(x==0||x==2000||y==0||y==2000) continue;        x-=way[j][0];y-=way[j][1];        f[0][++g]=x;        f[1][g]=y;        f[2][g]=j;      }    d++;    if(g>end)      bfs(end+1,g);}main(){    freopen("ice.in","r",stdin);    freopen("ice.out","w",stdout);    scanf("%d%d%d%d%d",&n,&x1,&y1,&x2,&y2);    for(k=1;k<=n;k++)    {        scanf("%d%d%d%d",&t1,&t2,&t3,&t4);        for(i=t1;i<=t3;i++)          b[i][t2]=b[i][t4]=1;        for(i=t2;i<=t4;i++)          b[t1][i]=b[t3][i]=1;    }    for(i=0;i<=2000;i++) b[i][0]=b[0][i]=b[i][2000]=b[2000][i]=1;    f[0][1]=x1;f[1][1]=y1;f[2][1]=233;    bfs(1,1);    printf("0");}

我一开始的思路很简单,就是用一个布尔矩阵模拟。然而人家毕竟是CTSC出的题,再怎么着也不至于这么水呀,于是人家就故意不谢坐标范围。

下午老师讲题以后我又重做了一遍。一开始我自己脑补了一个坐标系,然而写完以后测试样例的时候发现程序死了。这让我一脸懵逼,画出图来一看,这个题目的描述简直有毒!我就不描述了,见图:

可以看到,这个坐标系里面x轴是竖着的,y轴是横着的。这和我想象的一点也不符合。

我相信,只要是一个正常人,看完样例以后一定会先想到的是这个样子。然而就像图里标出来的,左上和右下是矛盾的,这就很蛋疼了。我的代码本来是这个样子:

#include<cstdio>#include<cstdlib>#include<cstring>long long n,i,j,x1,y1,x2,y2,d,g=1,t,p;struct aa{    int x1,y1,x2,y2;}a[4002];bool b[4002][5];int f[16002][3];void pd(int n){    if(f[n][0]==x2&&f[n][1]==y2)    {        printf("%d",d);        exit(0);    }}int findl(int x,int y){    int i,s=1e9,p=1e9+7;//s:距离     for(i=1;i<=n;i++)      if(a[i].x2<x&&x-a[i].x2<s&&a[i].y1<=y&&y<=a[i].y2)      {        s=x-a[i].x2;        p=i;      }    return p;}int findr(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(a[i].x1>x&&a[i].x1-x<s&&a[i].y1<=y&&y<=a[i].y2)//不是a[i].y1<=y&&y<=a[i].y2,注意       {        s=a[i].x1-x;        p=i;      }    return p;}int findu(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(a[i].y2>y&&a[i].y2-y<s&&a[i].x1<=x&&x<=a[i].x2)      {        s=a[i].y2-y;        p=i;      }    return p;}int findd(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(a[i].y1<y&&y-a[i].y1<s&&a[i].x1<=x&&x<=a[i].x2)      {        s=y-a[i].y1;        p=i;      }    return p;}void bfs(int begin,int end){    int i,p;    for(i=begin;i<=end;i++) pd(i);    for(i=begin;i<=end;i++)      if(i==1)      {        p=findu(f[1][0],f[1][1]);        if(p!=1e9+7)        {            f[++g][0]=f[1][1];            f[g][1]=a[p].y2-1;            f[g][2]=2;//在下面         }        p=findd(f[1][0],f[1][1]);        if(p!=1e9+7)        {            f[++g][0]=f[1][1];            f[g][1]=a[p].y1+1;            f[g][2]=1;//在上面         }        p=findl(f[1][0],f[1][1]);        if(p!=1e9+7)        {            f[++g][0]=f[1][0];            f[g][1]=a[p].x2+1;            f[g][2]=4;//在右面         }        p=findr(f[1][0],f[1][1]);        if(p!=1e9+7)        {            f[++g][0]=f[1][0];            f[g][1]=a[p].x1-1;            f[g][2]=3;//在左面         }      }      else//现在贴在某一冰山上         if(f[i][2]==1||f[i][2]==2)//现在只能左右滑动         {            p=findl(f[i][0],f[i][1]);            if(p!=1e9+7)            {                f[++g][0]=f[i][0];                f[g][1]=a[p].x2+1;                f[g][2]=4;            }            p=findr(f[i][0],f[i][1]);            if(p!=1e9+7)            {                f[++g][0]=a[p].x1-1;                f[g][1]=f[i][1];                f[g][2]=3;            }        }        else//现在只能上下滑动         {            p=findu(f[i][0],f[i][1]);            if(p!=1e9+7)            {                f[++g][0]=a[p].y2-1;                f[g][1]=f[i][1];                f[g][2]=2;            }            p=findd(f[i][0],f[i][1]);            if(p!=1e9+7)            {                f[++g][0]=a[p].y1+1;                f[g][1]=f[i][1];                f[g][2]=1;            }        }    d++;    bfs(end+1,g);}main(){    scanf("%d%d%d%d%d",&n,&x1,&y1,&x2,&y2);    for(i=1;i<=n;i++)      scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);    a[++n].x1=x2;a[n].x2=x2;a[n].y1=a[n].y2=y2;    memset(b,1,sizeof(b));    f[1][0]=x1;f[1][1]=y1;f[1][2]=-1;    bfs(1,1);    printf("0");}

本蒟蒻修改了n遍程序仍然死循环,这时候有好心人提醒我说,不要管原图的错误,按照上面的编就行了。修改后的find是这个样子:

int findl(int x,int y){    int i,s=1e9,p=1e9+7;//s:距离     for(i=1;i<=n;i++)      if(a[i].y2<y&&y-a[i].y2<s&&a[i].x1<=x&&x<=a[i].x2)      {        s=y-a[i].y2;        p=i;      }    return p;}int findr(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(a[i].y1>y&&a[i].y1-y<s&&a[i].x1<=x&&x<=a[i].x2)      {        s=a[i].y1-y;        p=i;      }    return p;}int findu(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(x>a[i].x2&&x-a[i].x2<s&&a[i].y1<=y&&y<=a[i].y2)      {        s=x-a[i].x2;        p=i;      }    return p;}int findd(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(x<a[i].x1&&a[i].x1-x<s&&a[i].y1<=y&&y<=a[i].y2)      {        s=a[i].x1-x;        p=i;      }    return p;}

然而这样仍然是错误的。为什么呢?因为我是把结束点也就是深洞当做一个冰山来看待的,这也就导致程序能成功地找到通向洞口最近的路,但并不能停止递归,于是成功地导致了死循环。继续修改,代码成了这个样子:

#include<cstdio>#include<cstdlib>#include<cstring>long long n,i,j,x1,y1,x2,y2,d,g=1,t,p;struct aa{    int x1,y1,x2,y2;}a[4002];bool b[4002][5];int f[16002][3];void print(){    printf("%d",d);    exit(0);}int findl(int x,int y){    int i,s=1e9,p=1e9+7;//s:距离     for(i=1;i<=n;i++)      if(a[i].y2<y&&y-a[i].y2<s&&a[i].x1<=x&&x<=a[i].x2)      {        if(i==n) print();        s=y-a[i].y2;        p=i;      }    return p;}int findr(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(a[i].y1>y&&a[i].y1-y<s&&a[i].x1<=x&&x<=a[i].x2)      {        if(i==n) print();        s=a[i].y1-y;        p=i;      }    return p;}int findu(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(x>a[i].x2&&x-a[i].x2<s&&a[i].y1<=y&&y<=a[i].y2)      {        if(i==n) print();        s=x-a[i].x2;        p=i;      }    return p;}int findd(int x,int y){    int i,s=1e9,p=1e9+7;    for(i=1;i<=n;i++)      if(x<a[i].x1&&a[i].x1-x<s&&a[i].y1<=y&&y<=a[i].y2)      {        if(i==n) print();        s=a[i].x1-x;        p=i;      }    return p;}void bfs(int begin,int end){    int i,p;    d++;    for(i=begin;i<=end;i++)      if(i==1)      {        p=findu(f[1][0],f[1][1]);        if(p!=1e9+7&&b[p][2])        {            f[++g][0]=a[p].x2+1;            f[g][1]=f[1][1];            f[g][2]=2;//在下面             b[p][2]=0;        }        p=findd(f[1][0],f[1][1]);        if(p!=1e9+7&&b[p][1])        {            f[++g][0]=a[p].x1-1;            f[g][1]=f[1][1];            f[g][2]=1;//在上面             b[p][1]=0;        }        p=findl(f[1][0],f[1][1]);        if(p!=1e9+7&&b[p][4])        {            f[++g][0]=f[1][0];            f[g][1]=a[p].y2+1;            f[g][2]=4;//在右面             b[p][4]=0;        }        p=findr(f[1][0],f[1][1]);        if(p!=1e9+7&&b[p][3])        {            f[++g][0]=f[1][0];            f[g][1]=a[p].y1-1;            f[g][2]=3;//在左面             b[p][3]=0;        }      }      else//现在贴在某一冰山上         if(f[i][2]==1||f[i][2]==2)//现在只能左右滑动         {            p=findl(f[i][0],f[i][1]);            if(p!=1e9+7&&b[p][4])            {                f[++g][0]=f[i][0];                f[g][1]=a[p].y2+1;                f[g][2]=4;                b[p][4]=0;            }            p=findr(f[i][0],f[i][1]);            if(p!=1e9+7&&b[p][3])            {                f[++g][0]=f[i][0];                f[g][1]=a[p].y1-1;                f[g][2]=3;                b[p][3]=0;            }        }        else//现在只能上下滑动         {            p=findu(f[i][0],f[i][1]);            if(p!=1e9+7&&b[p][2])            {                f[++g][0]=a[p].x2+1;                f[g][1]=f[i][1];                f[g][2]=2;                b[p][2]=0;            }            p=findd(f[i][0],f[i][1]);            if(p!=1e9+7&&b[p][1])            {                f[++g][0]=a[p].x1-1;                f[g][1]=f[i][1];                f[g][2]=1;                b[p][1]=0;            }        }    bfs(end+1,g);}main(){    scanf("%d%d%d%d%d",&n,&x1,&y1,&x2,&y2);    for(i=1;i<=n;i++)      scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);    a[++n].x1=x2;a[n].x2=x2;a[n].y1=a[n].y2=y2;    memset(b,1,sizeof(b));    f[1][0]=x1;f[1][1]=y1;f[1][2]=-1;    bfs(1,1);    printf("0");}

然而还是WA。经过反复查看,我发现我的程序在无解时是会无限递归下去的,于是主程序里的那个printf(“0”)并无卵用。所以我在每一个find里都加入了一个判断(举个例子):

void print(){    printf("%d",d);    exit(0);}int findl(int x,int y){    int i,s=1e9,p=1e9+7;//s:距离     for(i=1;i<=n;i++)      if(a[i].y2<y&&y-a[i].y2<s&&a[i].x1<=x&&x<=a[i].x2)      {        if(i==n) print();        s=y-a[i].y2;        p=i;      }    return p;}

其实我也应该反思一下,考试的时候自己为什么没有想到可以不用布尔数组?那样的话适应范围是很小的。后来重做的时候一开始我忘记了使用判重数组,这又意味着什么?没有及时推出递归导致死循环,说明我在做题的时候还是没有很好地理解题意。

0 0
原创粉丝点击