本周内容居然这么快就完成了,我可太厉害辽(●’◡’●)
深度优先搜索(Depth-First-Search):
从起点开始,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不了就后退,此种路径搜索策略就称为“深度优先搜索”,简称“深搜”。
其实称为“远度优先搜索”更容易理解些。因为这种策略能往前走一步就往前走一步,总是试图走得更远。所谓远近(或深度),就是以距离起点的步数来衡量的。
在图上寻找路径
代码:
Node path[MAX_LEN];// MAX-LEN取节点总数即可
int depth;
bool Dfs(V){
if(V为终点) {
path[depth] = V;
return true;
}
if(V为旧点)
return false;
将V标记为旧点;
path[depth] = V;
++ depth;
对和v相邻的每个节点U {
if(Dfs(U) == true)
return true;
}
-- depth;// 从V出发走不到终点,回退V的父节点
return false;
int main()
{
将所有点都标记为新点;
depth = 0;
if(Dfs(起点)) {
for(int i = 0; i <= depth; ++ i)
cout << path[i] << endl;
}
}
遍历图上所有节点:
代码:
Dfs(V) {
if(V是旧点)
return;
将v标记为旧点;
对和V相邻的每个点U {
Dfs(U);
}
}
int main() {
将所有点都标记为新点;
while(在图中能找到新点k)
Dfs(k);
}
图的表示方法——邻接表
用一个二维数组G存放图,G[i][j]表示节点i和节点j之间边的情况(如有无边,边方向,权值大小等)。遍历复杂度:O(n^2),n为节点数目。
每个节点V对应一个一维数组,里面存放从V连出去的边,边的信息包括另一顶点。还可能包含边权值等。遍历复杂度:O(n+e),n为节点数目,e为边数目。
例题1:城堡问题
描述:
右图是一个城堡的地形图(城堡四面都是墙).请你编写一个程序,计算城堡一共有多少房间,最大的房间多大。城堡被分割为m*n(m <= 50,n <= 50)个方块,每个方块可以有0~4面墙。
1 2 3 4 5 6 7
#############################
1 # | # | # | | #
#####---#####---#---#####---#
2 # # | # # # # #
#---#####---#####---#####---#
3 # | | # # # # #
#---#########---#####---#---#
4 # # | | | | # #
#############################
(图 1)
# = Wall
| = No wall
- = No wall
输入:
输出:
样例输入:
4
7
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13
样例输出:
5
9
解题思路:
1 1 2 2 3 3 3
1 1 1 2 3 4 3
1 1 1 5 3 5 3
1 5 5 5 5 5 3
代码:
#include
#include
#include
using namespace std;
int R,C; //行列数
int rooms[60][60]; //保存图信息
int color[60][60]; //方块是否染色过的标记
int maxRoomArea = 0;
int roomNum = 0;
int roomArea; //正在探索的房间面积
void Dfs(int i, int k) {
if(color[i][k]) //方块被走过
return;
++ roomArea;
color[i][k] = roomNum;
//与操作
if((rooms[i][k] & 1) == 0) Dfs(i,k-1); //向西走
if((rooms[i][k] & 2) == 0) Dfs(i-1,k); //向北走
if((rooms[i][k] & 4) == 0) Dfs(i,k+1); //向东
if((rooms[i][k] & 8) == 0) Dfs(i+1,k); //向南
}
int main()
{
cin >> R >> C;
for(int i = 1;i <= R; ++ i)
for(int k = 1;k <= C;++ k)
cin >> rooms[i][k];
memset(color,0,sizeof(color)); //注意不要漏掉
for(int i = 1; i <= R; ++ i)
for(int k = 1; k <= C; ++ k) {
if(!color[i][k]) {
++ roomNum;
roomArea = 0; //每次清零
Dfs(i,k); //一个房间探索完毕
maxRoomArea = max(roomArea,maxRoomArea); //更新最大值
}
}
cout << roomNum << endl;
cout << maxRoomArea << endl;
return 0;
}
例题2 踩方格:
描述:
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a、每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b、走过的格子立即塌陷无法再走第二次;
c、只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。
输入
允许在方格上行走的步数n(n≤20)。
输出
计算出的方案数量。
输入样例:
2
输出样例:
7
思路:
递归
从(i,j)出发,走n步的方案数,等于以下三项之和:
从(i+1,j)出发,走n-1步的方案数。前提:(i+1,j)还没走过
从(i,j+1)出发,走n-1步的方案数。前提:(i,j+1)还没走过
从(i,j-1)出发,走n-1步的方案数。前提:(i,j-1)还没走过
代码:
#include
#include
using namespace std;
int visited[30][50];
int ways(int i, int j, int n)
{
if(n == 0) //不走
return 1;
visited[i][j] = 1; //可以走
int num = 0;
if(! visited[i][j-1]) num += ways(i,j-1,n-1); //向西
if(! visited[i][j+1]) num += ways(i,j+1,n-1); //向东
if(! visited[i+1][j]) num += ways(i+1,j,n-1); //向北
visited[i][j] = 0; //标志清零
return num;
}
int main()
{
int n;
cin >> n;
memeset(visited,0,sizeof(visited));
cout << ways(0,25,n) << endl;
return 0;
}
课后练习1:红与黑
描述:
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
输入:
包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下
1)‘.’:黑色的瓷砖;
2)‘#’:白色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。
输出:
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
样例输入:
6 9
…#.
…#
…
…
…
…
…
#@…#
.#…#.
0 0
样例输出:
45
代码:
#include
#include
using namespace std;
char maze[30][30];
int visited[30][30];
int area = 0;
int W,H;
void dfs(int r,int c)
{
if( r < 0|| c < 0 || r >= H || c >= W ) //如果坐标不在范围内
return;
if( maze[r][c] == '#' || visited[r][c]) //如果瓷砖为白色或者已经走过
return;
++area;
visited[r][c] = 1;
dfs(r+1,c); //向南
dfs(r-1,c); //向北
dfs(r,c+1); //向东
dfs(r,c-1); //向西
}
int main()
{
while(1) {
cin >> W >> H;
if( W == 0 && H == 0)
break;
memset(visited,0,sizeof(visited));
area = 0;
int sr,sc;
for(int i = 0;i < H; ++ i)
for(int j = 0; j < W; ++ j) {
cin >> maze[i][j];
if( maze[i][j] == '@') {
sr = i;
sc = j;
}
}
dfs(sr,sc);
cout << area << endl;
}
return 0;
}
课后练习2: A Knight’s Journey
描述:
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.
输入:
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, . . .
第一行输入样例个数,剩下行数输入棋盘行数和列数。
输出:
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.
输出走完全盘的路径。
样例输入:
3
1 1
2 3
4 3
样例输出:
Scenario #1:
A1Scenario #2:
impossibleScenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4
解题思路:
注意骑士的走法为“日字型”,即:
一 左走一格向前走两格,二 左走一格向后走两格,
三 左走两格向前走一格,四 左走两格向后走一格,
五 右走一格向前走两格,六 右走一格向后走两格,
七 右走两格向前走一格,八 右走两格向后走一格。
代码:
#include
#include
#include
using namespace std;
int visited[30][30];
struct Pos
{
int r,c;
};
Pos path[30];
Pos dir[8] = {{-2,-1},{-2,1},{-1,-2},{-1,2},
{1,-2},{1,2},{ 2,-1},{2,1}};
int p,q;
bool Dfs(int r, int c,int step)
{//从 (r,c)出发,此时已经走了step步,看能否成功
if( step == p * q )
return true;
if( r < 0 || r >= q || c < 0 || c >= p )
return false;
if( visited[r][c] )
return false;
visited[r][c] = 1;
path[step].r = r;
path[step].c = c;
for( int i = 0;i < 8; ++ i ) {
if ( Dfs( r + dir[i].r,c + dir[i].c,step + 1 ))
return true;
}
visited[r][c] = 0; //回溯,取消这一步的走法,使得走其他步的时候,能绕回到这里
return false;
}
int main()
{
int t;
cin >> t;
for( int tt = 1; tt <= t; ++ tt ) {
cout << "Scenario #" << tt << ":" << endl;
cin >> p >> q; //p行q列
memset(visited,0,sizeof(visited));
int i;
for( i = 0; i < q; ++ i )
for( int j = 0; j < p; ++ j ) { //注意i,j,p,q的对应关系
if (Dfs(i,j,0)) {
i = q + 10; //用于区分是否走完全程
for( int k = 0; k < p * q; ++ k)
cout << char (path[k].r + 'A')
<< (path[k].c + 1) ;
break;
}
}
if( i == q ) //未走完全程
cout << "impossible";
cout << endl << endl;
}
return 0;
}
课后练习3: 棋盘问题:
描述:
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。
要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列。
请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
输入:
输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
输出:
对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C < 2^31)。
输入样例:
2 1
#.
.#
4 4
…#
…#.
.#…
#…
-1 -1
输出样例:
2
1
解题思路:
参考https://blog.csdn.net/xcflytosky/article/details/29222791
代码:
#include
#include
#include
using namespace std;
int n,k;
char board[10][10];
int total;
int chessPos[10];
int Dfs(int r,int x) //注意不能采用记忆化递归,因为不满足无后效性
{//从第r行开始摆,还有 x个棋子未摆
if( x == 0)
return 1;
if( n - r < x ) //肯定摆不下了
return 0;
total = 0;
for( int j = 0; j < n; ++ j ) { //要摆放第r行棋子,则遍历列,对原有棋盘进行调整
if( board[r][j] == '#' ) { //寻找棋子
int i;
for( i = 0; i < r; ++ i ) //前几行的棋子有无摆放重复列
if( chessPos[i] == j )
break;
if( i == r ) {
chessPos[r] = j;
total += Dfs(i+1,x-1);
}
}
}
// 不摆放第r行棋子,状态位清空,跳到下一行
chessPos[r] = -1;
total += Dfs(r+1,x);
return total;
}
int main()
{
while( true) {
cin >> n >> k;
if( n == -1 )
break;
total = 0;
memset(chessPos,0xff,sizeof(chessPos));
for( int i = 0;i < n; ++i)
for(int j = 0; j < n; ++j )
cin >> board[i][j];
cout << Dfs(0,k) << endl; //注意函数返回值
}
return 0;
}