问题描述
给定N*N的迷宫(3<=N<=7),有1~M个必经节点(2<=M<=5)。其中1为起点,M为终点。迷宫中除了必经节点1~M,0表示通路,可以经过,9表示障碍物,必须绕行。在寻找下一必经节点的时候,可以经过其他必经节点。要求计算出从1开始按数字大小顺序经过每一个必经节点到达终点的最短路径的个数
输入描述
输入T个迷宫,迷宫的大小为N*N,迷宫中必经节点为1~M。
输入样例
5
5 4
1 0 0 0 0
0 0 0 0 0
0 2 0 4 0
9 0 0 9 0
0 0 0 3 0
3 3
1 0 0
0 2 9
0 9 3
6 5
0 0 0 0 0 9
0 0 0 0 9 0
0 0 0 5 0 0
2 9 9 0 0 9
3 0 0 0 0 0
1 0 0 0 0 4
7 2
0 0 0 0 0 0 0
0 9 9 9 9 9 0
0 0 0 0 0 9 0
0 9 9 0 0 9 0
0 9 0 0 2 9 0
0 9 9 9 9 9 0
0 0 0 0 0 0 1
7 5
1 3 0 0 0 0 0
9 0 0 0 0 0 0
0 0 5 0 0 0 0
0 0 0 9 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 4
0 0 0 0 0 0 2
输出样例
#1 18
#2 0
#3 42
#4 3
#5 156508320
算法思路
BFS+队列,每个队列节点包括当前点的坐标(x,y)和当前节点所在的访问层数num和当前节点想找的下一个必经节点next。
设置访问数组visit[7][7]、visit1[7][7]、visit2[7][7]、visit3[7][7]用于记录1->2、2->3、3->4、4->5的访问情况。访问数组初始值都为-1,当第一次访问的时候,记录当前访问层数,如果后续访问层数>已经记录的层数,说明当前一定不是最短路径,直接结束本次循环。
当访问到终点,最短路径标志flag+1。
其他思路见代码。。以后有时间再加。。
代码实现(思路+测试疯狂注释版)
#include <iostream>
using namespace std;
int testcase;
int map[7][7];
int visit[7][7];
int visit1[7][7];
int visit2[7][7];
int visit3[7][7];
int flag;
int Answer = 1;
int N, M;
int answer[6];
int minLength[6];
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };
const int X = 9999;
struct record
{
int x;
int y;
int next;
int num;
};
typedef struct QueNode
{
record data[X];
int front;
int rear;
} *SeQue;
SeQue S;
//SeQue S1;
//SeQue S2;
//SeQue S3;
void InitQue(SeQue S)
{
S->front = S->rear = 0;
}
bool QueEmpty(SeQue S)
{
if (S->front == S->rear)
return true;
else
return false;
}
bool QueFull(SeQue S)
{
if ((S->rear + 1) % X == S->front)
return true;
else
return false;
}
void push(SeQue S, record val)
{
if (!QueFull(S))
{
S->data[S->rear] = val;
S->rear = (S->rear + 1) % X;
}
}
void pop(SeQue S)
{
if (!QueEmpty(S))
S->front = (S->front + 1) % X;
}
int QueLength(SeQue S)
{
int length;
length = (S->rear - S->front + X) % X;
return length;
}
void Ans()
{
for (int i = 2; answer[i] > 0, i <= M; i++)
{
//cout << "第" << i << "个answer:" << answer[i] << endl;
Answer *= answer[i];
}
}
void print()
{
cout << "#" << testcase << " ";
if (flag == 0)
{
cout << 0 << endl;
}
else
{
Ans();
cout << Answer << endl;
}
}
// BFS(当前层数, 下一个要找的位置)
void BFS()
{
while (!QueEmpty(S))
{
// 一切以队头操作为准
//cout << "=========" << endl;
record pos = S->data[S->front];
//cout << "当前处理节点位置:(" << pos.x << "," << pos.y << "), 当前处理节点值:" << map[pos.x][pos.y] << " 当前想找的下一个节点值:" << pos.next << " 当前节点层数:" << pos.num << endl;
//cout << "当前队头位置:" << S->front << " 当前队头的值:(" << S->data[S->front].x << "," << S->data[S->front].y << "), 当前队尾位置:" << S->rear << endl;
pop(S);
//cout << "出队后当前队头位置:" << S->front << " 当前队头的值:(" << S->data[S->front].x << "," << S->data[S->front].y << "), 当前队尾位置:" << S->rear << endl;
//比较层数,如果现在访问的节点是以前已经访问过的且这次访问的层数比之前的高,说明这个是走回去了。
//cout << "看看访问数组的值?";
if (pos.next == 2)
{
//cout << visit[pos.x][pos.y] << endl;
if (visit[pos.x][pos.y] == -1)
{
//cout << "寻找位置2,该节点是第一次访问,记录节点的访问层数" << endl;
visit[pos.x][pos.y] = pos.num; // 记录节点的访问层数
}
else if ((pos.num > visit[pos.x][pos.y]) || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
{
//cout << "continue 1" << endl;
continue;
}
}
if (pos.next == 3)
{
//cout << visit1[pos.x][pos.y] << endl;
if (visit1[pos.x][pos.y] == -1)
{
//cout << "寻找位置3,该节点是第一次访问,记录节点的访问层数" << endl;
visit1[pos.x][pos.y] = pos.num; // 记录节点的访问层数
}
else if (pos.num > visit1[pos.x][pos.y] || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
{
//cout << "continue 1" << endl;
continue;
}
}
if (pos.next == 4)
{
//cout << visit2[pos.x][pos.y] << endl;
if (visit2[pos.x][pos.y] == -1)
{
//cout << "寻找位置4,该节点是第一次访问,记录节点的访问层数" << endl;
visit2[pos.x][pos.y] = pos.num; // 记录节点的访问层数
}
else if (pos.num > visit2[pos.x][pos.y] || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
{
//cout << "continue 1" << endl;
continue;
}
}
if (pos.next == 5)
{
//cout << visit3[pos.x][pos.y] << endl;
if (visit3[pos.x][pos.y] == -1)
{
//cout << "寻找位置5,该节点是第一次访问,记录节点的访问层数" << endl;
visit3[pos.x][pos.y] = pos.num; // 记录节点的访问层数
}
else if (pos.num > visit3[pos.x][pos.y] || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
{
//cout << "continue 1" << endl;
continue;
}
}
// 如果走到终点了
//出现问题:如果前面节点还没访问完就恰好遇到终点,会提前记录,这不对
//加上条件:必须此时下一个要找的也是终点
if ((map[pos.x][pos.y] == M) && (pos.next == M))
{
//cout << "到终点了" << endl;
flag++;
//如果是第一次到这个点,说明是最短路径,记录最短值
if (answer[M] == 0)
{
minLength[M] = pos.num;
answer[M]++;
}
//如果不是第一次到这个点,则比较当前步数和已记录的最小值,如果相等则路径+1
else if (pos.num == minLength[M])
{
answer[M]++;
}
else
{
//cout << "continue 2" << endl;
continue;
}
}
//如果位置非法,则跳过
else if ((pos.x < 0) || (pos.y < 0) || (pos.x >= N) || (pos.y >= N) || map[pos.x][pos.y] == 9)
{
//cout << "continue 3" << endl;
continue;
}
//
else if (map[pos.x][pos.y] == pos.next)
{
/*pos.num = 0;
pos.next += 1;
push(S1, pos);*/
//visit:记录1->2的访问记录
//visit1:记录2->3的访问记录
//visit2:记录3->4的访问记录
//visit3:记录4->5的访问记录
if (pos.next == 2)
{
visit1[pos.x][pos.y] = 0;
}
if (pos.next == 3)
{
visit2[pos.x][pos.y] = 0;
}
if (pos.next == 4)
{
visit3[pos.x][pos.y] = 0;
}
//cout << "找到下一个了" << endl;
if (answer[pos.next] == 0)
{
//cout << "第一次找到下一个,记录最短步数:" << pos.num << endl;
minLength[pos.next] = pos.num;
//cout << "看看是否记录了?位置:minLength[" << pos.next << "] = " << minLength[pos.next] << endl;
answer[pos.next]++;
//cout << "看看是否记录到答案里了?answer[" << pos.next << "] = " << answer[pos.next] << endl;
//防止之前没经过必经点已经先访问到经过必经点后出来的点导致访问失败了,且路径是一段一段算的
//所以这里新插入的点路径长度置为1,这样一定可以避免这个问题
//第二个问题:这时候再往后走如果又走回去会发生问题,当前找到下一个点的最短路径记录的是上一个找过来的
//如果继续往后走又走回来会更短,被记录进去
//如果已经处理找到这个点的下一个点了,前面能走的应该都走完了吧?
//如果找到这个后把visit值归0应该不会有影响?
//但是这里是第一次找到,在这里归0不行,不然打个标记?
//不然从你开始新的一轮BFS?这个好像可以...这样也不好,这得写多少层函数啊?不然DFS+BFS呢、、
//可以多建几个visit表,根据next来确定访问哪个visit表
for (int i = 0; i < 4; i++)
{
int new_x = pos.x + dx[i];
int new_y = pos.y + dy[i];
if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
{
record newpos{};
newpos.x = new_x;
newpos.y = new_y;
newpos.next = pos.next + 1;
newpos.num = 1;
//cout << "push(" << newpos.x << "," << newpos.y << ")" << " push进去的点当前层数:" << newpos.num << endl;
push(S, newpos);
}
}
}
else if (pos.num == minLength[pos.next])
{
answer[pos.next]++;
//cout << "看看是否记录到答案里了?answer[" << pos.next << "] = " << answer[pos.next] << endl;
//重复记录了
/*for (int i = 0; i < 4; i++)
{
int new_x = pos.x + dx[i];
int new_y = pos.y + dy[i];
if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
{
record newpos{};
newpos.x = new_x;
newpos.y = new_y;
newpos.next = pos.next + 1;
newpos.num = pos.num + 1;
cout << "push(" << newpos.x << "," << newpos.y << ")" << endl;
push(S, newpos);
}
}*/
}
else
{
//cout << "continue 4" << endl;
continue;
}
}
else //if ((map[pos.x][pos.y] == 0) || (map[pos.x][pos.y] != pos.next))
{
//cout << "这个点可以走通" << endl;
for (int i = 0; i < 4; i++)
{
int new_x = pos.x + dx[i];
int new_y = pos.y + dy[i];
if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
{
record newpos{};
newpos.x = new_x;
newpos.y = new_y;
newpos.next = pos.next;
newpos.num = pos.num + 1;
//cout << "push(" << newpos.x << "," << newpos.y << ")" << " push进去的点当前层数:" << newpos.num << endl;
push(S, newpos);
}
}
}
}
}
int main()
{
S = new QueNode;
/*S1 = new QueNode;
S2 = new QueNode;
S3 = new QueNode;*/
InitQue(S);
/*InitQue(S1);
InitQue(S2);
InitQue(S3);*/
for (int i = 0; i < 7; i++)
for (int j = 0; j < 7; j++)
{
visit[i][j] = -1;
visit1[i][j] = -1;
visit2[i][j] = -1;
visit3[i][j] = -1;
}
testcase++;
int T;
record start{};
//cout << "Input the number of maps: ";
cin >> T;
//cout << "No." << testcase << ": Input the size of the map and the destination point number" << endl;
for (testcase; testcase <= T; testcase++)
{
cin >> N >> M;
//cout << "Input the map (0 for empty, 1 for start, 9 for obstacle):" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
cin >> map[i][j];
if (map[i][j] == 1)
{
start.x = i;
start.y = j;
start.next = 2;
start.num = 0;
}
}
}
//cout << "push(" << start.x << "," << start.y << ")" << " push进去的点当前层数:" << start.num << endl;
push(S, start);
/*for (int i = 0; i < 4; i++)
{
int new_x = start.x + dx[i];
int new_y = start.y + dy[i];
if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
{
record newpos{};
newpos.x = new_x;
newpos.y = new_y;
newpos.next = start.next;
newpos.num = start.num + 1;
cout << "push(" << newpos.x << "," << newpos.y << ")" << endl;
push(S, newpos);
}
}*/
BFS();
print();
//打印完之后要记得清空用到的中间过程。
flag = 0;
Answer = 1;
for(int i=0;i<7;i++)
for (int j = 0; j < 7; j++)
{
map[i][j] = 0;
visit[i][j] = -1;
visit1[i][j] = -1;
visit2[i][j] = -1;
visit3[i][j] = -1;
}
for (int i = 0; i < 6; i++)
{
minLength[i] = 0;
answer[i] = 0;
}
}
return 0;
}
去掉注释版的就不放了,代码包含小白每一步的思路和遇到的问题,最终完成了这个不难的问题。
运行结果