1713: Lu Chi's Single Xiao Han

Topic link

Title description
Xiaohan is very smart, so he plays ACM very well, and often secretly learns late at night.
He studies so hard that he has no time to visit the school completely.
One day, he heard that the black swan of Keda Lake was very beautiful, and because he had no girlfriend, he went there alone.
However, he was still concentrating on watching the black swan, and he didn't realize that the training camp was about to start in k minutes. Unfortunately, Xiaohan happened to be a road idiot.
Do you think he can rush to the maker to participate in the training camp in k minutes?
If so, how long will it take him at least to return to the makerspace to participate in the training camp? How many paths are there like this?

Enter the
first group of test data as T (1 <= T <= 100), which means the number of test sample groups.
For each set of test examples: the
first line of input is three positive integers nmk (1 <= n, m <= 1000, 0 <= k <= 10000), n, m indicate the length and width of the map, and k indicates the maximum Time spent on the road is allowed (the time spent on the road is just k, which is also legal).
Enter the map in the next n lines, which contains the symbols'*','#','L', and'C'.
*: Indicates the space where walking is allowed.
#: indicates an obstacle, a space where you cannot move around.
L: It means the Lake of Science and Technology, the starting point of Xiaohan, and there is only one.
C: Indicates the maker space, that is, the end of Xiaohan. It is guaranteed that there is one and only one.
The timing of Xiao Han's movement is one minute from one space to another. At the beginning, Xiaohan was already standing at the starting point. When the travel time is equal to k, the end point is still considered legal.
(Note: Xiaohan can only move in four directions, up, down, left, and right.)

Output The
output format is "Case #x:" (without quotation marks), and x means the xth sample.
If Xiaohan cannot return to the maker within k minutes, -1 is output.
Otherwise, output the shortest time it takes Xiaohan to return to the maker, and the number of paths that satisfy the shortest time.

Sample input
4
2 2 3
L*
C
4 4 10
L
**
C
4 4 8
L
##
#
*
###*
C***
4 4 10
L*##
#***
###*
C* **
Sample output
Case #1: 2 2
Case #2: 6 20
Case #3: -1
Case #4: 9 1

Title

Find the number of shortest path options

Ideas:

① To find the shortest path, you can use bfs(), but it requires the number of options (there are different paths to the same point, and the time is the same and the shortest), consider the cost to complete the traversal of the same layer, and record the number of walks Circle, that is, how much time it took.

The points that have been walked cannot be marked as visited immediately, but marked as visited after leaving the team, because bfs() is going out in a circle, each circle can correspond to a time, and the time spent in this circle The time is the time of the previous lap+1. If the next step of the previous lap is not completed, this point may be reached again in the previous lap, so it cannot be immediately marked as unable to go again to prevent a point from returning to the previous lap, so when the circle where the point is located has been completed, It does not need to go again in the last lap, that is, after each point is out of the team, it can be marked as visited (not allowed to be re-entered).

AC code

#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;
    
}

②You can use dfs() to memorize search + 3 pruning (there are in the code)

Before performing a deep search on a point, mark it as passed to prevent two points from going back and forth in an endless loop. After searching a point deeply, mark this point as not passed again, because other search schemes may reach this point. It can take less time.

AC code

#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;
}

Guess you like

Origin blog.csdn.net/RunningBeef/article/details/114242738
lu