小游戏 (最少线段连线问题)

 游戏在一个分割成w * h个长方格子的矩形板上进行。如图所示,每个长方格子上可以有一张游戏卡片,也可以没有。当下面的情况满足时,我们认为两个游戏卡片之间有一条路径相连: 
   路径只包含水平或者竖直的直线段。路径不能穿过别的游戏卡片。但是允许路径临时离开矩形板。 
   下面是一个例子:
这里在(1, 3)和(4, 4)处的游戏卡片是可以相连的。而在 (2, 3) 和 (3, 4) 处的游戏卡是不相连的,因为连接他们的每条路径都必须要穿过别的游戏卡片。 

   你现在要在小游戏里面判断是否存在一条满足题意的路径能连接给定的两个游戏卡片。


这道题最早遇见是在高二的县里的模拟赛的时候,感觉是一个经典的问题。

分析,首先因为要求是最少的线段,实际就是用最少的转弯,那就每个方向光搜出去应该是能到的最远处内的所有点都是应该在该层中被加入队列的。

于是按照线段的转折次数为层数进行光搜即可。其中要注意一些细节。


该题出现在P大的编程网格上,岁月匆匆感慨万千,因为细节没有处理好,导致大数据上WA了若干次。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstring>
#include <queue>


using namespace std;


const int maxN = 100+10;
const int dir[4][2]= {0,1,0,-1,-1,0,1,0};


char s[maxN], map[maxN][maxN];
int sx,sy,tx,ty,n,m,ans;
int f[maxN][maxN][4],arrive[maxN][maxN][4];
struct Node{
       int x,y,Dir;       
}node,newNode;
queue<Node> q;
bool inside(int x,int y)
{
       return (x>=0 && x<=n+1 && y>=0 && y<=m+1);  
}
bool ok()
{
       memset(arrive,0,sizeof(arrive));
       memset(f,127,sizeof(f));
       bool canArrive = false;
       while (!q.empty()) q.pop();
       for (int k = 0; k < 4;++k)
       {
           node.x = sx; node.y = sy; node.Dir = k;
           q.push(node);
           f[sx][sy][k]=1;
           arrive[sx][sy][k]=true;                 
       }
       while (!q.empty())
       {
           Node now = q.front();
           q.pop();
           arrive[now.x][now.y][now.Dir] == false;
           for (int k = 0; k < 4; ++k)
           for (int step = 1; step <= maxN ; ++step)
           {
               int x=now.x+dir[k][0]*step;
               int y=now.y+dir[k][1]*step;
               if ( !inside(x,y)) break;
               if (tx == x && ty == y) 
               {
                    f[x][y][k]=min(f[x][y][k],
                    f[now.x][now.y][now.Dir]+(now.Dir != k));
                    canArrive = true; 
               }
               if (map[x][y] == 'X' ) break;
               if (f[now.x][now.y][now.Dir]+(now.Dir != k)< f[x][y][k])
               {
                    f[x][y][k]=f[now.x][now.y][now.Dir]+(now.Dir != k);
                    if (!arrive[x][y][k])
                    {
                       arrive[x][y][k]=true;
                       newNode.x = x; newNode.y = y; newNode.Dir = k;
                       q.push(newNode);
                    }                  
               }                  
           }                    
       }   
       return canArrive;
}
int main()
{
          int Test = 0;
          while (scanf("%d%d",&m,&n)==2)
          {
                if (m==0  && n==0) break;
                getchar();
                printf("Board #%d:\n",++Test);
                for (int i = 0; i <= n+1; ++i)
                    for (int j = 0; j <= m+1; ++j) map[i][j]='S';
                for (int i = 1;i <= n; ++i) 
                {
                         gets(s);
                         for (int j = 1; j <= m ;++j)
                             map[i][j] = s[j-1];
                }
                
                int Pair = 0;
                while(scanf("%d%d%d%d",&sy,&sx,&ty,&tx))
                {
                       if (sx+sy+tx+ty == 0) break;
                       if (!ok()) printf("Pair %d: impossible.\n",++Pair);
                       else 
                       {
                            int ans = maxN * maxN;
                            for (int k = 0 ; k < 4; ++k)
                            ans=min(ans,f[tx][ty][k]);
                            printf("Pair %d: %d segments.\n",++Pair,ans);     
                       }   
                }    
          }
          return 0;    
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值