1713: 路痴的单身小菡

题目链接

题目描述
小菡很聪明,所以他打ACM非常给力,经常偷偷学习到深夜
他是如此的努力学习,以至于他根本就没有时间完整的逛过学校。
有一天,他听说科大湖的黑天鹅非常好看,由于没有女朋友,他便独自一个人去了。
然而他还在专心致志的观赏黑天鹅,丝毫没有意识到集训还有 k 分钟就要开始了,不幸的是刚好小菡是一个路痴。
你觉得他在 k 分钟内可以赶到创客参加集训吗?
如果可以,他最少要花多少时间才可以回到创客空间参加集训呢?这样子的路径有多少条?

输入
测试数据第一组为T(1 <= T <= 100),表示测试样例组数。
对于每组测试样例:
第一行输入为三个正整数 n m k(1 <= n,m <= 1000,0 <= k <= 10000),n,m表示地图的长和宽,k表示最多允许花费在路上的时间(在路上花费的时间刚好为k也合法)。
接下来n行输入地图,其中包含有符号‘*’、‘#’、‘L’、‘C’。
*:表示可以允许走动的空间。
#:表示障碍物,无法走动的空间。
L:表示科大湖,即小菡的起点,保证有且仅有一个。
C:表示创客空间,即小菡的终点,保证有且仅有一个。
小菡走动的计时是从一个空间到另一个空间为一分钟。开始时,小菡已经站在了起点上。当移动时间等于k时刚好到达终点依然视为合法。
(注意:小菡只能往上下左右四个方向走动。)

输出
输出形式为“Case #x: ”(不包含引号),x表示对应第x个样例。
如果小菡在 k 分钟内无法回到创客,则输出-1。
否则,输出小菡回到创客花费的最短时间,和满足该最短时间的路径条数。

样例输入
4
2 2 3
L*
C
4 4 10
L
**
C
4 4 8
L
##
#
*
###*
C***
4 4 10
L*##
#***
###*
C***
样例输出
Case #1: 2 2
Case #2: 6 20
Case #3: -1
Case #4: 9 1

题意

求最短路径方案数

思路:

①求最短路可以用bfs(),但要求方案数(存在不同的路径到达同一点,且所花时间相同且最短),考虑花费为相同的一层遍历要完全走完,并且记录走了几圈,也就是花了多少时间。

对于走过的点不能立即标记为访问过,而是出队后再标记为访问过,因为bfs()是一圈一圈的往外走,每一圈可以对应成一个时间,这一圈所花的时间是上一圈的时间+1。如果上一圈的下一步没有全部走完,这个点可能会被上一圈再次走到,所以不能立即 标记为不能再走,防止一个点退回上一圈,所以当点所在圈已经走完,它的上一圈没必要再走一次,也就是每个点出队后就可以把它标记为访问过(不让它再被入队)。

AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char graph[1020][1020];//图
int visited[1020][1020];//记录所需时间
int MOVE[4][2] ={
    
    {
    
    -1,0},{
    
    1,0},{
    
    0,1},{
    
    0,-1}};
ll n,m,k;
ll ans;

struct point{
    
    //点的坐标
    int x,y;
};

bool right_index(int i,int j){
    
    //判断下标合法性
    return 0 <= i && i < n && 0 <= j && j < m;
}

int bfs(int i,int j){
    
       //从起点开始一圈一圈的往外扩张,外圈所花时间比里圈多1
    queue<point>q;
    point u,v;
    u.x = i;
    u.y = j;
    q.push(u);
                           
    int cnt = 0;
    int x1,y1;

    while(!q.empty() && cnt < k){
    
    
        u = q.front();
        q.pop();
       
        //cout << u.x << ' ' << u.y << endl;
        for(int i = 0;i < 4;i++){
    
    
            v.x = u.x + MOVE[i][0];
            v.y = u.y + MOVE[i][1];
            if(right_index(v.x,v.y)){
    
    
                if(graph[v.x][v.y] == '*'){
    
    
                    q.push(v);
                    visited[v.x][v.y] = visited[u.x][u.y] + 1;
        
                }else if(graph[v.x][v.y] == 'C'){
    
    
                    visited[v.x][v.y] = visited[u.x][u.y] + 1;
                    ans++;
                    x1 = v.x;//记录创客坐标
                    y1 = v.y;
                }
               
            }
        }
        graph[u.x][u.y] = '#'; //剪枝 已经开始走u的下一圈,防止点u再走

        if(!q.empty()){
    
    
            v = q.front();
            if(visited[u.x][u.y] != visited[v.x][v.y]){
    
    //如果点u和下一个点
                cnt++;                                  //到达的所需时间不同就说明
            }                                           //说明上个点是它那个圈的最后一个点
        }
    }

    if(ans)//如果找到路径 返回所需最短时间
        return visited[x1][y1];
    else return 0;
}

int main(){
    
    
    int T;
    int flag = 0;
    cin >> T;
    for(int t = 0;t < T;t++){
    
    
        ans = flag = 0;
        memset(graph,0,sizeof(graph));
        memset(visited,0,sizeof(visited));

        scanf("%d %d %d",&n,&m,&k);

        for(int i = 0;i < n;i++){
    
    
            scanf("%s",graph[i]);
            //cout << graph[i] << endl;
        }
           
        for(int i = 0;i < n;i++){
    
    
            for(int j = 0;j < m;j++){
    
    
                if(graph[i][j] =='L'){
    
    //找起点
                    k = bfs(i,j);
                    flag = 1;
                    break;
                }
            }
            if(flag)
                break;
        }

        if(ans)
                printf("Case #%d: %d %d\n",t+1,k,ans);
        else printf("Case #%d: -1\n",t+1);
    }
    //system("pause");
    return 0;
    
}

②可以用dfs()记忆化搜索 + 3个剪枝 (代码中有)

对一个点进行深搜前要将其标记为走过,防止两个点来回走死循环,对一个点深搜后,要把这个点再标记为没走过,因为别的搜索方案可能到这个点花的时间可以更少。

AC代码

#include <bits/stdc++.h>
using namespace std;
char path[1010][1010];
int visited[1010][1010];
int MOVE[4][2] = {
    
    {
    
    -1,0},{
    
    1,0},{
    
    0,-1},{
    
    0,1}};

int T,n,m,k;
int ans;
int x,y;        //记录创客坐标
int flag1,flag; //flag1记录是否已经早到最短路径

bool right_index(int i,int j){
    
    
    return 0 <= i && i < n && 0 <= j && j < m;
}

void dfs(int i,int j,int time){
    
    // 坐标 和已经使用的时间
    int i1,j1;

    if(time > k)//剪枝
        return ;
    if(visited[i][j] && visited[i][j] < time)//剪枝
            return ;
    visited[i][j] = time;

    if(flag1 && k-time < abs(x-i) + abs(y-j))//剪枝
        return ;

    if(path[i][j] == 'C'){
    
    
        if(time == k){
    
    
            ans++;
        }
        else{
    
           //time < k
            ans = 1;
            k = time;//更新最短距离
        }
        x = i;
        y = j;
        flag1 = 1;  //标记已经有了最短路径
        return ;
    }

    path[i][j] = '#';//把走过的路标记

    for(int g = 0;g  < 4;g++){
    
    
        i1 = i + MOVE[g][0];
        j1 = j + MOVE[g][1];
       
        if(right_index(i1,j1) && path[i1][j1] != '#'){
    
    
            dfs(i1,j1,time + 1);
        }
            
    }
        
    path[i][j] = '*';//退回上一步,所以标记为没有走过

    return ;
}

int main(){
    
    
    
    cin >> T;
    
    for(int t = 0;t < T;t++){
    
    

        memset(visited,0,sizeof(visited));
        ans = 0;

        cin >> n >> m >> k;
     
        for(int i = 0;i < n;i++){
    
    
           scanf("%s",path[i]);
        }
        
        flag1 = 0;
        flag = 0;
        for(int i = 0;i < n;i++){
    
    
            for(int j = 0;j < m;j++)
                if(path[i][j] == 'L'){
    
    
                    
                    dfs(i,j,0);
                    if(ans)
                        cout << "Case #"<< t+1 <<": " <<  k  << " " << ans << " " << endl;
                    else cout << "Case #"<< t+1 <<": " << -1 << endl;
                    flag = 1;
                    break;
                }
            if(flag)
                break;
        }
       
    }
    //system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/RunningBeef/article/details/114242738
今日推荐