CSL的校园卡-状态压缩+bfs
csl的校园卡
题目:
今天是阳光明媚,晴空万里的一天,CSL早早就高兴地起床走出寝室到校园里转悠。
但是,等到他回来的时候,发现他的校园卡不见了,于是他需要走遍校园寻找它的校园卡。CSL想要尽快地找回他掉的校园卡,于是便求助于OneDay帮他一起找。
OneDay和CSL在同一已知的地点出发,并以相同的速度(1格/秒)搜索校园,试求两人走遍校园的最短时间。
Input
第一行为两个整数n,m(1≤n,m≤4),表示地图的大小。接下来是n行m列的地图:X表示障碍物,S表示起点,O表示空地。障碍物不能直接经过,数据保证所有空地是可达的,起点有且只有一个。
Output
输出一个整数表示两人共同走遍校园所需的最少时间。
思路:
建立数组vis[id][x1][y1][x2][y2],id为走过的路径,压缩为一个值,x1表示A的当前x坐标,y1表示A的当前y坐标,x2表示B的当前x坐标,y2表示B的当前y坐标。对两个点进行bfs即可,注意计算路径id值时应该用或|进行计算。当id等于结束值时返回结果。
为什么要用或运算呢?
好了,现在考察一下理解,为什么在计算最终状态时,使用+号而没用|也能过呢?
代码:
#include <iostream>
#include <algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
int m, n;
char map[10][10];
struct node
{
int x1, y1;
int x2, y2;
int t;
int id;
}s;
bool vis[(1 << 16) + 1][5][5][5][5];
queue<node> q;
int dir[4][2] = { 1,0,-1,0,0,1,0,-1 };
int bfs(int end)
{
q.push(s);
vis[s.id][s.x1][s.y1][s.x2][s.y2] = true;
while (!q.empty())
{
node t = q.front();
q.pop();
if (t.id==end)
{
return t.t;
}
for (int i = 0; i < 4; i++)
{
int nx = t.x1 + dir[i][0], ny = t.y1 + dir[i][1];
if (nx >= 0 && ny >= 0 && nx < n && ny < m&&map[nx][ny]!='X')
{
for (int j = 0; j < 4; j++)
{
int n1x = t.x2 + dir[j][0], n1y = t.y2 + dir[j][1];
if (n1x >= 0 && n1y >= 0 && n1x < n && n1y < m&&map[n1x][n1y]!='X')
{
int newid;
newid = (t.id | (1 << (nx*m + ny))) | 1 << ((n1x*m + n1y));
if (vis[newid][nx][ny][n1x][n1y])
continue;
vis[newid][nx][ny][n1x][n1y] = true;
node b;
b.x1 = nx, b.y1 = ny;
b.x2 = n1x, b.y2 = n1y;
b.t = t.t + 1;
b.id = newid;
q.push(b);
}
}
}
}
}
}
int main()
{
memset(vis, false, sizeof vis);
scanf("%d %d", &n, &m);
int end = 0;
for (int i = 0; i < n; i++)
{
scanf("%s", &map[i][0]);
for (int j = 0; j < m; j++)
{
if (map[i][j] == 'S')
s.x1 =s.x2= i, s.y1=s.y2 = j, end += 1 << (i*m + j);//注意
if (map[i][j] == 'O')
end += 1 << (i*m + j);//注意
}
}
s.t = 0;
s.id = 1<<(s.x1*m+s.y1);
printf("%d\n", bfs(end));
}