CTSC2000 冰原探险

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

今天上午日常考试,第二题就是这个题目。
传送门
(其实我是在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;
}

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值