题意:
考虑一个被分为 W × H 网格的房间,机器人是矩形的,大小为 1 × 1。房间内的格子被分为干净的,脏的和障碍物三种。机器人不能踏上障碍物,而机器人只要经过一个脏的格子,它就会被清洁成干净的格子。
机器人每步可以向四个方向(上、下、左、右)之一走一步,进入在这个方向上和它相邻的格子。机器人可以经过一个格子多次。
你的任务是求得机器人清理干净整个房间(即把所有的脏格子清洁成干净格子)至少需要走多少步。输入文件包含多个测试点。
每个测试点的第一行包含两个空格分隔的整数 W 和 H。之后 H 行,每行包含一个长度是 W 字符的字符串,字符串中的每个字符表示房间的一个格子,且为以下字符之一:
.:干净格子;
*:脏格子;
x:障碍物;
o:机器人所在位置;
机器人所在位置是一个干净格子。
输入文件以 0 0 结尾。对于每个测试点,在单独的一行内输出答案。
如果无解,输出 -1。
TSP问题好难的样子,好像要用什么蚁群算法?
不过这种数据范围小的可以直接用状压DP水过。
标准做法就是f[i][S]表示当前在i,未经过的点的集合是S时还需要最少的步数。
对于这道题,因为脏点不超过10个,所以可以用状压DP,复杂度O(2^10*10)(因为起点确定,所以是10不是10^2)。
先预处理出TSP问题中的n^2条边,就是每两个脏点之间的最短路。之后直接DP即可。
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
int n, m, num, d[405][405], pos[15], f[15][1<<11];
char map[405];
bool vis[405];
queue <int> q;
int id(int x, int y){
return (x-1)*m + y;
}
void BFS(int i){
vis[i] = 1;
d[i][i] = 0;
q.push(i);
while(!q.empty()){
int u = q.front(); q.pop();
while(map[u] == 'x' && !q.empty())
{u = q.front(); q.pop();}
if(map[u] == 'x') break;
if(u+1 <= id(n, m) && !vis[u+1] && u % m){
d[i][u+1] = d[i][u] + 1;
vis[u+1] = 1; q.push(u+1);
}
if(u-1 > 0 && !vis[u-1] && u % m != 1){
d[i][u-1] = d[i][u] + 1;
vis[u-1] = 1; q.push(u-1);
}
if(u+m <= id(n, m) && !vis[u+m]){
d[i][u+m] = d[i][u] + 1;
vis[u+m] = 1; q.push(u+m);
}
if(u-m > 0 && !vis[u-m]){
d[i][u-m] = d[i][u] + 1;
vis[u-m] = 1; q.push(u-m);
}
}
}
int dp(int i, int j){
if(f[i][j] >= 0) return f[i][j];
f[i][j] = 1 << 30;
for(int k = 0; k < num; k++) if((j>>k)&1){
f[i][j] = min(f[i][j], dp(k+1, j-(1<<k)) + d[pos[i]][pos[k+1]]);
}
return f[i][j];
}
int main()
{
while(scanf("%d %d", &m, &n) && n){
memset( d,-1, sizeof d);
memset( f,-1, sizeof f);
memset(pos, 0, sizeof pos);
memset(map, 0, sizeof map);
num = 0;
for(int i = 1; i <= n; i++){
scanf("%s", map+id(i, 1));
}
for(int i = id(n, m); i; i--){
if(map[i] == 'o') pos[0] = i;
if(map[i] == '*') pos[++num] = i;
}
bool flag = 0;
for(int i = 0; i <= num; i++){
memset(vis, 0, sizeof vis); BFS(pos[i]);
for(int j = 0; j <= num; j++){
if(d[pos[i]][pos[j]] < 0){
flag = 1; break;
}
}
} if(flag) {puts("-1"); continue;}
int beg = 0;
for(int i = 0; i <= num; i++){
if(i != num) beg += (1 << i);
f[i][0] = 0;
for(int j = 0; j < num; j++){
if(i == j+1) continue;
f[i][1<<j] = d[pos[i]][pos[j+1]];
}
}
printf("%d\n", dp(0, beg));
}
return 0;
}