Cpp环境【POJ2435】【CQYZOJ3034】【USACO2005 U S Open Silver】Navigating the City城市交通

Description 【问题描述】


  A dip in the milk market has forced the cows to move to the city. The only employment available is in the venerable field of taxi-driving. Help the cows learn their way around the city.
  Given a city map with E (1 <= E <= 40) east/west street locations and N (1 <= N <= 30) north/south street locations, create instructions for a taxi driver to navigate from the start of his route (marked ‘S’) to the end (marked ‘E’). Each instruction is a direction (one of ‘N’, ‘E’, ‘S’, or ‘W’) followed by a space followed by an integer that tells how many blocks to drive in that direction. If multiple routes are available, your program should output the shortest route. The shortest route is guaranteed to exist and be unique.
  The map is depicted as a grid of ‘+’s that represent intersections and a set of roads depicted as ‘-’ and ‘|’. Buildings and other obstacles are shown as ‘.’s. Here is a typical map:
  
+-+-+.+-+-+
|…|…..|
+-+.+-+-+-+
..|…….|
S-+-+-+.E-+

  The taxi should go east, north, west, north, east two blocks, and so on. See the output format and sample solution below for its complete route.


  由于奶牛市场的需求,奶牛必须前往城市,但是唯一可用的交通工具是出租车。教会奶牛如何在城市打的。

  给出一个城市的地图,东西街区E条,南北街区N条。制作一个开车指南给出租车司机,告诉他如何从起点(用S表示)到终点(用E表示)。每一个条目用空格分开成两部分,第一个部分是方向(N,E,S,W之一),第二个是一个整数,表示要沿着这个方向开几个十字路口。如果存在多条路线,你应该给出最短的。数据保证,最短的路径存在且唯一。

  地图中’+’表示十字路口,道路用’|’和’-‘表示。建筑物和其他设施用’.’表示,下面是一张地图:
    +-+-+.+-+-+
    |…|…..|
    +-+.+-+-+-+
    ..|…….|
    S-+-+-+.E-+
    
  出租车可以沿着东、北、西、北,东开两个十字路口,依次类推,具体将有样例给出。(输入数据保证路径唯一)   

Input 【输入格式】

  • Line 1: Two space-separated integers, N and E.
  • Lines 2..2*N: These lines each contain 2*E-1 characters and encode the map of the street. Every other input line gives the data for the east/west streets; the remaining lines show the north/south streets. The format should be clear from the example.

第1行:两个用空格各开的整数N和E。
第2到第2N行:每行有2E-1个字符,表示地图。

Output 【输出格式】

  • Lines 1..?: Each line contains a direction letter and a number of blocks to travel in that direction.

  每行有一个表示方向的字母和一个表示要开几个十字路口的数字表示。

Sample Input 【输入样例】

3 6
+-+-+.+-+-+
|…|…..|
+-+.+-+-+-+
..|…….|
S-+-+-+.E-+

Sample Output 【输出样例】

E 1
N 2
W 1
N 1
E 2
S 1
E 3
S 1
W 1

【数据范围】

1<=E<=40 , 1<=N<=30

【原题传送矩阵】

POJ2435 Navigating the City 原题传送矩阵
CQYZOJ 3034 原题传送矩阵

【思路梳理】

  水题,首先分别从起点S、终点E出发进行bfs/dfs计算每一个结点到起点S的距离dist1、到终点E的距离dist2,再运用“最短路径上的点i满足dist1[i]+dist2[i]==起点终点间的距离”原理(题目保证有唯一解),标记最短路径上的每一个结点即可,最后运用dfs(bfs亦可,但感觉实现稍显复杂,不能运用dfs能够在递归调用时传递参数的优势)输出路径即可。
  输出路径时注意些小细节——比如说从起点开始的第一个结点的方向。

【Cpp代码】

#include
#include
#include
#include
#define maxn 85
using namespace std;
int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};
struct loca{int x,y;}st,ed;
int map[maxn][maxn],n,e,dist1[maxn][maxn],dist2[maxn][maxn];
bool vis[maxn][maxn],mark[maxn][maxn];
vector<char>ans;

loca q[maxn*maxn];
int rear,front;
void bfs(loca t,int cmd)
{
    memset(vis,false,sizeof(vis));
    rear=front=1;
    q[rear++]=t;
    vis[t.x][t.y]=true;
    if(cmd) dist1[t.x][t.y]=0;//标记距离,用于确定最短路径
    else dist2[t.x][t.y]=0;
    while(rear!=front)
    {
        t=q[front++];
        loca tt;
        for(int i=0;i<4;i++)
        {
            tt.x=t.x+dx[i],tt.y=t.y+dy[i];
            if(tt.x<1 || tt.x>2*n-1 || tt.y<1 || tt.y>2*e-1 || map[tt.x][tt.y]==0)  continue;//越界或者没有道路
            if(map[t.x][t.y]==2 && (i==0 || i==1))  continue;//道路只能东西通,而此时在非法地南北向移动
            if(map[t.x][t.y]==3 && (i==2 || i==3))  continue;//道路只能南北通,而此时在非法地东西向移动
            if(vis[tt.x][tt.y]) continue;

            vis[tt.x][tt.y]=true;
            if(cmd) dist1[tt.x][tt.y]=dist1[t.x][t.y]+1;
            else dist2[tt.x][tt.y]=dist2[t.x][t.y]+1;
            q[rear++]=tt;//新点进队列
        }
    }
}

void out(int dir,int d)
{
    if(dir==0)      printf("N");
    else if(dir==1) printf("S");
    else if(dir==2) printf("W");
    else if(dir==3) printf("E");
    printf(" %d\n",d);
}

void dfs_path(loca now,int dir,int d)
{
    int x=now.x,y=now.y;
    for(int i=0;i<4;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(!mark[xx][yy])   continue;
        mark[xx][yy]=false;

        if(map[xx][yy]==1)//如果当前正在路口
        {
            if(dir==-1) dir=i;//还没有标记过方向
            if(dir==i)  d++;//沿着原来的方向移动了1个路口
            else  out(dir,d),d=1,dir=i;//与原来方向不一致,打印一次路径并将d还原为1(思考为什么不是0)
        }
        dfs_path((loca){xx,yy},dir,d);
    }
    if(x==ed.x && y==ed.y)  out(dir,d);//终点需要格外打一次
}

void find_path()
{
    for(int i=1;i<=2*n-1;i++)
    for(int j=1;j<=2*e-1;j++)if(dist1[i][j]+dist2[i][j]==dist1[ed.x][ed.y]) 
        mark[i][j]=true;//mark[i][j]=true表示(i.j)在最优路径上

    mark[st.x][st.y]=false;//注意把起点的mark标记清掉
    dfs_path(st,-1,0);//dfs
}

int main()
{
//  freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&e);
    memset(map,0,sizeof(map));
    memset(dist1,0x3f,sizeof(dist1));
    memset(dist2,0x3f,sizeof(dist2));
    for(int i=1;i<=2*n-1;i++)
    for(int j=1;j<=2*e-1;j++)//字符读入
    {
        char ch;ch=getchar();
        while(ch!='-' && ch!='|' && ch!='+' && ch!='.' && ch!='S' && ch!='E')   ch=getchar();//注意处理奇怪字符
        if(ch=='S')     st.x=i,st.y=j,map[i][j]=1;//起点
        else if(ch=='E')ed.x=i,ed.y=j,map[i][j]=1;//终点
        else if(ch=='-')    map[i][j]=2;
        else if(ch=='|')    map[i][j]=3;
        else if(ch=='+')    map[i][j]=1;
        else if(ch=='.')    map[i][j]=0;
        /*map[i][j]的值不同,表示(i,j)在图中的连通方式不同。
        1表示交叉路口,上下左右四通,2表示左右双通,3表示上下双通,0表示无法通过*/
    }
    bfs(st,1);//忘记怎么传递二维数组做参数了,⊙﹏⊙b汗
    bfs(ed,0);//只好用一个flag来进行控制,flag=1表示是从起点开始正向bfs,flag=0表示从终点开始反向bfs
    find_path();
    return 0;
}

你可能感兴趣的:(cpp,poj,棋盘的dfs-bfs,最优路径,路径打印,数据结构之三,图型结构,基础算法之一,查找搜索&排序,难度评级,Casual)