搜索之(DFS,BFS)01
关于他们的思想,这里就不再罗嗦了,直接 show you my code ,看题讨论 。
题目1: 5×5迷宫 + 保存路径
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,
只能横着走或竖着走,不能斜着走,
要求编程序找出从左上角到右下角的最短路线。
Input
一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
Output
左上角到右下角的最短路径,格式如样例所示。
Sample Input
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
Sample Output
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)
解题思路:一看到是求解最短路径,直接BFS,可是怎么存储他所走过的路径呐?说实话,这可难了我老长时间。后来经过我二哥点播,得到了下面的思路。
BFS的下一层(无论有多少个节点)存储上一层的数组下标即可,如下图。最后就可以通过数组下标找到上一个经过的节点是什么。最后可以通过递归的形式输出最短路径。
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <stdio.h>
/*根据广度优先搜索的话,搜索到终点时,该路径一定是最短的*/
using namespace std;
struct Node
{
int x, y, before;
Node() = default;
Node(int _x, int _y, int _index) : x(_x), y(_y), before(_index) {}
bool check()
{
if (x < 0 || x > 4 || y < 0 || y > 4)
return false;
else
return true;
}
} result[20]; //记录路径
int maze[5][5] = {0}; //迷宫
int index = 0;
bool bfs(Node &start, Node &end)
{
bool visted[5][5] = {false}; //标记数组
int dir[4][2] = {
{0, 1},
{1, 0},
{0, -1},
{-1, 0}};
queue<Node> QQ;
QQ.push(start);
visted[start.x][start.y] = true;
Node Vnow, Vnext;
while (!QQ.empty())
{
Vnow = QQ.front();
QQ.pop();
result[index].x = Vnow.x;
result[index].y = Vnow.y;
result[index].before = Vnow.before;
index++;
for (int i = 0; i < 4; ++i)
{
Node Vnext(Vnow.x + dir[i][0], Vnow.y + dir[i][1], index - 1);// 注意这里
if (Vnext.x == end.x && Vnext.y == end.y)
{
result[index].x = end.x;
result[index].y = end.y;
result[index].before = index - 1;
return true;
}
if (Vnext.check() && !visted[Vnext.x][Vnext.y] && !maze[Vnext.x][Vnext.y])
{
QQ.push(Vnext);
visted[Vnext.x][Vnext.y] = true;
}
}
}
//数据保证有唯一解
}
void output(Node tmp)
{
if (tmp.before == 0 && tmp.x == 0 && tmp.y == 0)
{
printf("(%d, %d)\n", tmp.x, tmp.y);
}
else
{
output(result[tmp.before]);
printf("(%d, %d)\n", tmp.x, tmp.y);
}
}
int main(void)
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
cin >> maze[i][j];
}
}
Node start(0, 0, 0);
Node end(4, 4, 0);
bfs(start, end);
output(result[index]);
}
题目2:B - 闪现! + 分层(输出层数)
Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (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 points X - 1 or X + 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 and K
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.
题目大意:可向前走一步,也可向后走一步,还可以( 向前走两倍的现在步数 )
解题思路:因为我们找的还是最快的方式,所以我们还是采取BFS的策略。这个与上一道题有所不同的是“不用存储所走过的路径”,所以我们只用关注他的层数,最后将层数输出即可 。具体的可看代码。
//思路: 因为是找最值,所以策略是 BFS
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
#define MAX 100001 //草,原来那么多的RuntimeError,全是因为我数组开得太大了造成的,草草草草
int n = 0, k = 0;
int result = 0; //存放最终结果
bool visted[MAX] = {false};
int BFS(int start) // 1
{
queue<int> QQ;
QQ.push(start);
visted[start] = true;
int Vnow, Vnext;
while (!QQ.empty())
{
int loop_size = QQ.size();
for (int j = 0; j < loop_size; j++) //有多少出多少,分层处理
{
Vnow = QQ.front();
QQ.pop();
for (int i = 0; i < 3; i++)
{
if (i == 0)
Vnext = Vnow + 1;
if (i == 1)
Vnext = Vnow - 1;
if (i == 2)
Vnext = Vnow * 2;
if (Vnext == k)
return 0;
if ( Vnext >= 0 && Vnext < MAX && visted[Vnext] == false ) //这里最好是先判断出没出界,然后判断访问过没有
{
QQ.push(Vnext);
visted[Vnext] = true;
}
}
}
result++;
}
}
int main(void)
{
while (cin >> n >> k)
{
result = 0;
memset(visted, 0, sizeof(visted));
if (n >= k)
{
cout << n - k << endl;
continue;
}
BFS(n);
cout << result + 1 << endl;
}
return 0;
}
题目3:C - 不规则的棋盘 + 不同行不同列
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放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
解题思路:DFS。这道题与八皇后问题相当,就是要让他们不同行不同列,所以可以采取:”将列进行标记,按行进行搜索“,这里需要特别注意的一点是:要正确的解决,k < n
的情况,还是ACMER的那种做法,给一个全局变量,DFS要退出时,给他减一下,最后更加需要注意的是在循环结束的时候还需要往下一行DFS一下才行。(当然还是为了解决k < n
的情况啦)
//思路: DFS + 不同行不同列
#include <iostream>
using namespace std;
char map[10][10] = {0};
int n = 0, k = 0; // n*n 的棋盘,k个棋子
int count = 0; // 记录方案数目
int temp = 0;
bool col[10] = {false}; //对列进行标记,然后按行进行搜索
int DFS(int x)
{
if (temp == k)
{
count++;
return 0;
}
if ( x >= n ) //超出界限
return 0;
for (int i = 0; i < n; i++) //控制列
{
if (map[x][i] == '#' && col[i] == false)
{
col[i] = true;
temp++;
DFS(x + 1);
col[i] = false;
temp--;
}
}
DFS(x + 1);
/*为了避免:
n= 2,k=1
.#
#.
的情况
*/
return 0;
}
int main(void)
{
while (cin >> n >> k)
{
if (n == -1 && k == -1)
return 0;
for (int i = 0; i < n; i++)
scanf("%s", map[i]);
count = 0;
DFS(0); //从第0行开始
cout << count << endl;
}
return 0;
}
题目4:D - 二进制? + 思维
Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal representation contains only the digits 0 and 1. You may assume that n is not greater than 200 and there is a corresponding m containing no more than 100 decimal digits.
Input
The input file may contain multiple test cases. Each line contains a value of n (1 <= n <= 200). A line containing a zero terminates the input.
Output
For each value of n in the input print a line containing the corresponding value of m. The decimal representation of m must not contain more than 100 digits. If there are multiple solutions for a given value of n, any one of them is acceptable.
Sample Input
2
6
19
0
Sample Output
10
100100100100100100
111111111111111111
题目大意:这个可以说是我不得不写题目大意的一道题。给一个n,找到一个m 使得他只有0和1组成,且是n的倍数。
解题思路:这个里面,因为由0和1组成的数字是不会重复的,所以就不需要标记了,从1开始将他们一个一个放入队列即可,找到能够整除n的m即可 。
#include <iostream>
#include <queue>
using namespace std;
long long n; //输入的 n
queue<long long> QQ; //rtm,刚开始一直不过,把queue提出来一下就过了,草
long long BFS(long long start)
{
QQ.push(start);
long long Vnow;
while (!QQ.empty())
{
Vnow = QQ.front();
QQ.pop();
if (Vnow % n == 0)
return Vnow;
if ((Vnow*10) % n == 0)
return Vnow * 10;
if ((Vnow*10+1) % n == 0)
return Vnow * 10 + 1 ;
QQ.push(Vnow * 10);
QQ.push(Vnow * 10 + 1);
}
return 0;
}
int main(void)
{
while (cin >> n && n )
{
while (!QQ.empty())
{
QQ.pop();
}
cout << BFS(1) << endl;
}
return 0;
}