基础搜索算法题解(D-H)

66 篇文章 0 订阅


练习链接:http://acm.njupt.edu.cn/vjudge/contest/view.action?cid=171#overview



D题 A Knight's Journey

Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 32388
Accepted: 11027

Description

Background
The knight is getting bored of seeing the same black and white squares again and again and has decided to make a journey
around the world. Whenever a knight moves, it is two squares in one direction and one square perpendicular to this. The world of a knight is the chessboard he is living on. Our knight lives on a chessboard that has a smaller area than a regular 8 * 8 board, but it is still rectangular. Can you help this adventurous knight to make travel plans?

Problem
Find a path such that the knight visits every square once. The knight can start and end on any square of the board.

Input

The input begins with a positive integer n in the first line. The following lines contain n test cases. Each test case consists of a single line with two positive integers p and q, such that 1 <= p * q <= 26. This represents a p * q chessboard, where p describes how many different square numbers 1, . . . , p exist, q describes how many different square letters exist. These are the first q letters of the Latin alphabet: A, . . .

Output

The output for every scenario begins with a line containing "Scenario #i:", where i is the number of the scenario starting at 1. Then print a single line containing the lexicographically first path that visits all squares of the chessboard with knight moves followed by an empty line. The path should be given on a single line by concatenating the names of the visited squares. Each square name consists of a capital letter followed by a number.
If no such path exist, you should output impossible on a single line.

Sample Input

3
1 1
2 3
4 3

Sample Output

Scenario #1:
A1

Scenario #2:
impossible

Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4

Source

TUD Programming Contest 2005, Darmstadt, Germany

题目链接:http://poj.org/problem?id=2488

题目大意:给一个p*q的棋盘,行用数字表示,列用字母表示,求用马步遍历的遍历序列,马走“日”。

题目分析:没啥好说的,裸DFS,因为题目说了从任意一点开始都可以,说明解是一个圈,所以当然从A1开始搜字典序最小了,方向遍历时也要根据字典序来遍历,关键是记录路径,和k题类似,二维数组,一维用深度来记录路径

#include <cstdio>
#include <cstring>
int vis[30][30];
int path[30][2];
int p,q;
bool flag;
int dirx[8] = {-1,1,-2,2,-2,2,-1,1};
int diry[8] = {-2,-2,-1,-1,1,1,2,2};

void DFS(int x,int y, int step)
{
    path[step][0] = x;
    path[step][1] = y;
    if(step == p * q)
    {
        flag = true;
        return;
    }
    for(int i = 0; i < 8; i++)
    {
        int xx = x + dirx[i];
        int yy = y + diry[i];
        if(xx < 1 || yy < 1 || xx > p || yy > q || vis[xx][yy] || flag)
            continue;   
        vis[xx][yy] = 1;
        DFS(xx, yy, step + 1);
        vis[xx][yy] = 0;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int i = 1; i <= T; i++)
    {
        scanf("%d %d",&p, &q);
        memset(vis, 0, sizeof(vis));
        vis[1][1] = 1;
        flag = false;
        DFS(1, 1, 1);
        if(flag)
        {
            printf("Scenario #%d:\n",i);
            for(int i = 1; i <= p * q; i++)
                printf("%c%d", path[i][1] - 1 + 'A', path[i][0]);
            printf("\n");
        }
        else
            printf("Scenario #%d:\nimpossible\n",i);
        if(i != T)
            printf("\n");
    }
}




E题 Children of the Candy Corn

Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 10145
Accepted: 4405

Description

The cornfield maze is a popular Halloween treat. Visitors are shown the entrance and must wander through the maze facing zombies, chainsaw-wielding psychopaths, hippies, and other terrors on their quest to find the exit.

One popular maze-walking strategy guarantees that the visitor will eventually find the exit. Simply choose either the right or left wall, and follow it. Of course, there's no guarantee which strategy (left or right) will be better, and the path taken is seldom the most efficient. (It also doesn't work on mazes with exits that are not on the edge; those types of mazes are not represented in this problem.)

As the proprieter of a cornfield that is about to be converted into a maze, you'd like to have a computer program that can determine the left and right-hand paths along with the shortest path so that you can figure out which layout has the best chance of confounding visitors.

Input

Input to this problem will begin with a line containing a single integer n indicating the number of mazes. Each maze will consist of one line with a width, w, and height, h (3 <= w, h <= 40), followed by h lines of w characters each that represent the maze layout. Walls are represented by hash marks ('#'), empty space by periods ('.'), the start by an 'S' and the exit by an 'E'.

Exactly one 'S' and one 'E' will be present in the maze, and they will always be located along one of the maze edges and never in a corner. The maze will be fully enclosed by walls ('#'), with the only openings being the 'S' and 'E'. The 'S' and 'E' will also be separated by at least one wall ('#').

You may assume that the maze exit is always reachable from the start point.

Output

For each maze in the input, output on a single line the number of (not necessarily unique) squares that a person would visit (including the 'S' and 'E') for (in order) the left, right, and shortest paths, separated by a single space each. Movement from one square to another is only allowed in the horizontal or vertical direction; movement along the diagonals is not allowed.

Sample Input

2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
9 5
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########

Sample Output

37 5 5
17 17 9

Source

South Central USA 2006

题目链接:http://poj.org/problem?id=3083

题目大意:一个m*n的矩阵给出起点和终点求两点间左优先路,右优先路和最短路

题目分析:最短显然用BFS裸的,左右优先用DFS做,这题就是代码量大的水题


#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int n, m;
int sx, sy, ex, ey;
char map[42][42];
bool vis[42][42];
int dx[] = {1, 0, -1, 0}; //方向不能反!
int dy[] = {0, 1, 0, -1};
struct Node
{
    int x, y;
    int step;
};
queue <Node> q;
bool flag;
int l, r; //记录左优先和右优先的解

int BFS()  //求最短
{
    memset(vis, 0, sizeof(vis));
    Node t, cur, st;
    st.x = sx;
    st.y = sy;
    vis[sx][sy] = true;
    st.step = 1;
    q.push(st);
    while(!q.empty())
    {
        cur = q.front();
        q.pop();
        if(cur.x == ex && cur.y == ey)
        {
            while(!q.empty())
                q.pop();
            return cur.step;
        }
        for(int i = 0; i < 4; i++)
        {
            t.x = cur.x + dx[i];
            t.y = cur.y + dy[i];
            t.step = cur.step + 1;
            if(t.x < n && t.y < m && t.x >= 0 && t.y >= 0 && !vis[t.x][t.y] && map[t.x][t.y] != '#')
            {
                vis[t.x][t.y] = true;
                q.push(t);
            }
        }
    }
    return 0;
}

int DFS(int x, int y, int dir)
{
    if(x == ex && y == ey)
        return 1;
    for(int i = 0; i < 4; i++)
    {
        int xx = x + dx[dir];
        int yy = y + dy[dir];
        if(map[xx][yy] == '.' || map[xx][yy] == 'E')
        {
            if(flag)
            {
                l++;
                if(dir == 3)
                    dir = -1;
                if(DFS(xx, yy, dir + 1))
                    return 1;
            }
            else
            {
                r++;
                if(dir == 0)
                    dir = 4;
                if(DFS(xx, yy, dir - 1))
                    return 1;
            }
        }
        if(flag)
        {
            dir--;
            if(dir == -1)
                dir = 3;
        }
        else
        {
            dir++;
            if(dir == 4)
                dir = 0;
        }
    }
    return 0;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &m, &n);
        for(int i = 0; i < n; i++)
        {
            scanf("%s", map[i]);
            for(int j = 0; j < m; j++)
            {
                if(map[i][j] == 'S')
                {
                    sx = i;
                    sy = j;
                }
                if(map[i][j] == 'E')
                {
                    ex = i;
                    ey = j;
                }
            }
        }
        flag = true;
        l = r = 1;
        DFS(sx, sy, 0);
        flag = false;
        DFS(sx, sy, 0);
        printf("%d %d %d\n", l, r, BFS());
    }
}




F题 Curling 2.0

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 12420 Accepted: 5246

Description

On Planet MM-21, after their Olympic games this year, curling is getting popular. But the rules are somewhat different from ours. The game is played on an ice game board on which a square mesh is marked. They use only a single stone. The purpose of the game is to lead the stone from the start to the goal with the minimum number of moves.

Fig. 1 shows an example of a game board. Some squares may be occupied with blocks. There are two special squares namely the start and the goal, which are not occupied with blocks. (These two squares are distinct.) Once the stone begins to move, it will proceed until it hits a block. In order to bring the stone to the goal, you may have to stop the stone by hitting it against a block, and throw again.


Fig. 1: Example of board (S: start, G: goal)

The movement of the stone obeys the following rules:

  • At the beginning, the stone stands still at the start square.
  • The movements of the stone are restricted to x and y directions. Diagonal moves are prohibited.
  • When the stone stands still, you can make it moving by throwing it. You may throw it to any direction unless it is blocked immediately(Fig. 2(a)).
  • Once thrown, the stone keeps moving to the same direction until one of the following occurs:
    • The stone hits a block (Fig. 2(b), (c)).
      • The stone stops at the square next to the block it hit.
      • The block disappears.
    • The stone gets out of the board.
      • The game ends in failure.
    • The stone reaches the goal square.
      • The stone stops there and the game ends in success.
  • You cannot throw the stone more than 10 times in a game. If the stone does not reach the goal in 10 moves, the game ends in failure.


Fig. 2: Stone movements

Under the rules, we would like to know whether the stone at the start can reach the goal and, if yes, the minimum number of moves required.

With the initial configuration shown in Fig. 1, 4 moves are required to bring the stone from the start to the goal. The route is shown in Fig. 3(a). Notice when the stone reaches the goal, the board configuration has changed as in Fig. 3(b).


Fig. 3: The solution for Fig. D-1 and the final board configuration

Input

The input is a sequence of datasets. The end of the input is indicated by a line containing two zeros separated by a space. The number of datasets never exceeds 100.

Each dataset is formatted as follows.

the width(=w) and the height(=h) of the board
First row of the board

...
h-th row of the board

The width and the height of the board satisfy: 2 <=w <= 20, 1 <=h <= 20.

Each line consists of w decimal numbers delimited by a space. The number describes the status of the corresponding square.

0 vacant square
1 block
2 start position
3 goal position

The dataset for Fig. D-1 is as follows:

6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1

Output

For each dataset, print a line having a decimal integer indicating the minimum number of moves along a route from the start to the goal. If there are no such routes, print -1 instead. Each line should not have any character other than this number.

Sample Input

2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0

Sample Output

1
4
-1
4
10
-1

Source


题目链接:http://poj.org/problem?id=3009


题目大意:丢石头遇到障碍物停在障碍物的前一个格子中且障碍物消失,丢的条件是周围有一格为空,问从起点到终点最少丢多少次


题目分析:DFS条件注意下就行,这题是要先输入列再输入行
在这里附上两组数据:

6 2                       
2 0 1 1 1 1
1 1 1 1 1 3         输出: -1        
6 2
2 0 1 1 0 1        
1 1 1 1 3 1         输出: 4


#include <cstdio>
#include <cstring>
int dirx[4] = {0,-1,0,1}; 
int diry[4] = {-1,0,1,0};
int map[22][22];
int m, n;
int ans;

void DFS(int x, int y, int step)  //深度优先搜索算法
{
    if(step > 10)  //步数大于10返回
        return;
    for(int i = 0; i < 4; i++)   //枚举各个方向
    {
        int xx = x + dirx[i];   
        int yy = y + diry[i];
        //判断是否出界,当前步数是否小于之前的步数,当前位置是否非法(必须有空格才能扔)
        if(step < ans && xx > 0 && xx <= m && yy > 0 && yy <= n && map[xx][yy] != 1)
        {
            //当没有出界且格子为空时,一直沿着当前方向走
            while(xx > 0 && xx <= m && yy > 0 && yy <= n && map[xx][yy] == 0)
            {
                xx += dirx[i];
                yy += diry[i];
            }
            //记录界外或者障碍物的前一步
            int xx2 = xx - dirx[i];
            int yy2 = yy - diry[i];
            //如果出界则回到for换方向
            //如果在界内且落在在终点将步数加1(因为从0开始的),这里不return,因为求最小要全部遍历完
            //如果在界内且落在障碍物上,对障碍物前一步继续进行深搜,此时将障碍物设为0(障碍物被清除)
            //DFS完还要将其设回为1,因为要遍历所有的情况.
            if(xx > 0 && xx <= m && yy > 0 && yy <= n)
            {
                if(map[xx][yy] == 3)
                    ans = step + 1;
                if(map[xx][yy] == 1)
                {
                    map[xx][yy] = 0;
                    DFS(xx2, yy2, step + 1);
                    map[xx][yy] = 1;
                }
            }
        }
    }
}

int main()
{
    int sx, sy;     //记录起点和终点的坐标
    while(scanf("%d %d", &n, &m) != EOF && (m + n))
    {
        ans = 11;
        for(int i = 1; i <= m; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                scanf("%d",&map[i][j]);
                if(map[i][j] == 2)
                {
                    map[i][j] = 0;   //找到起点记录下后将其值设为0
                    sx = i;
                    sy = j;
                }
            }
        }
        DFS(sx,sy,0);   //起点步数为0
        if(ans < 11)
            printf("%d\n",ans);
        else
            printf("-1\n");
    }
}




G题 棋盘问题

Time Limit: 1000MS
Memory Limit: 10000K
Total Submissions: 23958
Accepted: 11859

Description

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

Input

输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

Output

对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

Sample Input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1


题目链接:http://poj.org/problem?id=1321


题目大意:中文题,不解释了


题目分析:要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,采取行递增的方式搜索,只需要考察列的存储状态就行了,注意搜索一次后要复原即可。


#include <cstdio>
#include <cstring>
int n, k, ans;
char map[10][10];
bool vis[10]; //标记列是否已有棋子
void dfs(int row, int num) //当前行,已经安放的棋子个数
{
    if(num == k)
    {
        ans++;
        return;
    }
    for(int i = row; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            if(map[i][j] == '#' && !vis[j])
            {
                vis[j] = true;
                dfs(i + 1, num + 1);
                vis[j] = false;
            }
        }
    }
}
int main()
{
    while(scanf("%d %d",&n, &k) != EOF && (n + k) != -2)
    {
        ans = 0;
        memset(vis, false, sizeof(vis));
        for(int i = 0; i < n; i++)
            scanf("%s", map[i]);
        dfs(0, 0);
        printf("%d\n", ans);
    }
}




H题 Catch That Cow

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 49787 Accepted: 15623

Description

Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a pointN (0 ≤N ≤ 100,000) on a number line and the cow is at a pointK (0 ≤K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.

* Walking: FJ can move from any point X to the pointsX- 1 orX+ 1 in a single minute
* Teleporting: FJ can move from any point X to the point 2 × X in a single minute.

If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?

Input

Line 1: Two space-separated integers:N andK

Output

Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.

Sample Input

5 17

Sample Output

4

Hint

The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes.

Source


题目链接:http://poj.org/problem?id=3278


题目大意:一条线上,给一个起点和一个终点,有三种走法,走一次1秒,位置坐标加1,减1和乘2,求从起点到终点的最短时间


题目分析:裸BFS,有可能n >= k,这时候只需要求n-k即可,n < k时,搜一搜就行了,千万注意!!虽然肯定有解,但是在BFS函数最后还是要return一个值,不然会wa


#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int n, k;
bool vis[200005];

struct Node
{
    int num;
    int step;
};

int BFS()
{
    memset(vis, false, sizeof(vis));
    queue <Node> q;
    Node st, cur, t;
    st.num = n;
    st.step = 0;
    vis[n] = true;
    q.push(st);
    while(!q.empty())
    {
        t = q.front();
        cur = t;
        q.pop();
        if(t.num == k)
            return t.step;
        if(t.num + 1 < 200001 && !vis[t.num + 1])
        {
            vis[t.num + 1] = true;
            t.num ++;
            t.step ++;
            q.push(t);
        }
        t = cur;
        if(t.num - 1 >= 0 && !vis[t.num - 1])
        {
            vis[t.num - 1] = true;
            t.num --;
            t.step ++;
            q.push(t);
        }
        t = cur;
        if(t.num * 2 < 200001 && !vis[t.num * 2])
        {
            vis[t.num * 2] = true;
            t.num *= 2;
            t.step ++;
            q.push(t);
        }
    }
    return 0;
}

int main()
{
    while(scanf("%d %d", &n, &k) != EOF)
    {
        if(n >= k)
            printf("%d\n", n - k);
        else
            printf("%d\n", BFS());
    }
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值