题目大意:
给你一个无向图,一个机器人位于图中的点s位置,他的目标是点t位置,你可以移动他,每一次只能移动一个位置,图上有m个障碍物,这些障碍物也可以移动,这些障碍物分布在图上的不同点,任何一个点同一时刻只能有一个物品,不管是机器人还是障碍物。最后问最少需要移动几次(不只是机器人的移动,还包含障碍物的移动)。输出移动的过程,从哪个点到哪个点。
这个题说实话还挺难的,主要是因为题目中涉及到的可以移动的物品比较多,对于状态不好进行描述。一开始想的是每一步都尝试去移动机器人,机器人被堵住了再尝试去移动障碍物,但是发现这样的代码实现基本上是不可能的,其中的点太过于繁杂。
看了别人的代码才知道,其实我们没有必要去要求每一次移动的都是机器人。我们可以去尝试移动任何一个东西,不管他是机器人还是障碍物,因为BFS本来的思想也就是暴力,所以我们就尝试使用BFS去暴力的搜索就可以了,直到机器人到了目标位置,这个题的时间限制居然是10s。在搜索过程中还要记录每一次移动的哪个点的物品。
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
typedef struct{
// s用来记录当前机器人在图中的哪个位置
// t用来记录当前的障碍物的状态(包含机器人)
// pre用来记录当前状态的上一个状态在队列的位置
// length标记走到当前状态需要几步
int s,t,pre,length;
}Node;
typedef struct{
int size;
int rear;
int front;
// 队列的大小要能容纳
Node queue[1<<20];
}PriorityQueue;
PriorityQueue p;
int map[16][16];
// 标记某种状态是否被访问过
int vis[16][1<<15];
int n,m,s,t;
void push(Node d){
p.size++;
p.queue[p.rear++] = d;
}
Node pop(){
Node res = p.queue[p.front++];
p.size--;
return res;
}
// 获取路径,根据不同状态来计算当前是把哪个障碍物从哪移动到哪
void getRes(int d){
if (p.queue[d].pre != 0)
getRes(p.queue[d].pre);
// 开始状态
int from_s = p.queue[p.queue[d].pre].t;
// 结束状态
int to_s = p.queue[d].t;
int a, b;
// 处于二进制数字的哪一位
a = from_s ^ (from_s&to_s);
b = to_s ^ (from_s&to_s);
int u, k;
// 计算该位置对应的图中的位置
for (int i = 1; i <= n; i++){
if ((1 << i)&a) u = i;
if ((1 << i)&b) k = i;
}
printf("%d %d\n",u,k);
}
int main(){
int count;
scanf("%d",&count);
memset(vis,0,sizeof(vis));
for(int k=1;k<=count;k++){
p.front = 0;
p.rear = 0;
p.size = 0;
memset(map,0,sizeof(map));
scanf("%d%d%d%d",&n,&m,&s,&t);
int state = 0;
for(int i=0;i<m;i++){
int c;
scanf("%d",&c);
state |= (1<<c);
}
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
map[x][y] = 1;
map[y][x] = 1;
}
// 开始时障碍的布局
state |= (1 << s);
// 标记当前状态已经被访问过
// 为什么要用二维数组?因为我们不能仅仅通过一维01不能判断重复该状态是否出现过
// 虽然一个一维数组中可能状态值相同,但是机器人的位置可能并不同
// 所以需要一个二维数据来标记机器人在不同位置时的障碍物
vis[s][state] = k;
Node node;
node.s = s;
node.t = state;
node.pre = -1;
node.length = 0;
push(node);
Node res;
res.length = -1;
while(p.size>0){
Node node = pop();
// 如果当前点位于终点
if(node.s == t){
res = node;
break;
}
for(int i=1;i<=n;i++){
// 当前i位置存在障碍物,尝试移动当前位置的障碍物
// 注意这个障碍物可能是机器人
if((1 << i)&node.t){
for(int j=1;j<=n;j++){
if(map[i][j]){
// 当前要移动到的位置存在障碍物
if((1<<j) & node.t){
continue;
}
int curS = node.s,
// 新状态
curState = ((node.t | (1<<j)) ^ (1 << i));
// 当前障碍是机器人
if(curS == i){
// 机器人移动到新的位置了
curS = j;
}
if(vis[curS][curState]!=k){
vis[curS][curState] = k;
Node newNode;
newNode.t = curState;
newNode.s = curS;
newNode.pre = p.front-1;
newNode.length = node.length+1;
push(newNode);
}
}
}
}
}
}
printf("Case %d: ",k);
if(res.length == -1){
printf("-1\n");
}else{
printf("%d\n",res.length);
getRes(p.front-1);
}
printf("\n");
}
}