SP1702 CLEANRBT - Cleaning Robot POJ 2688 Cleaning Robot
洛谷 SP1702 CLEANRBT - Cleaning Robot
POJ 2688 Cleaning Robot
题目链接:洛谷 SP1702 CLEANRBT - Cleaning Robot
算法标签: 动态规划(DP)
、状态压缩
题目
题意翻译
在这里,我们要解决的问题是路径规划移动机器人清洁矩形房间地板。
考虑房间地板铺的是方形瓷砖,其尺寸适合清洁机器人(1×1)。这里有“干净瓷砖”和“脏瓷砖”,机器人可以把“脏瓷砖”变成“干净瓷砖”。也可能有一些占一个瓷砖大小的障碍(家具)在房间里。如果瓷砖上有障碍物,机器人就不能访问它。机器人移动到一个相邻的瓷砖一个移动。机器人移动的瓦片必须是与机器人所在的瓦片相邻的四个瓦片(即东、西、北或南)之一。机器人可以访问两次或更多的瓦片。
你的任务是编写一个程序,计算机器人的最小移动次数,如果可能的话,将所有的“脏瓷砖”改为“干净瓷砖”。
题目描述
Here, we want to solve path planning for a mobile robot cleaning a rectangular room floor with furniture.
Consider the room floor paved with square tiles whose size fits the cleaning robot (1 × 1). There are 'clean tiles' and 'dirty tiles', and the robot can change a 'dirty tile' to a 'clean tile' by visiting the tile. Also there may be some obstacles (furniture) whose size fits a tile in the room. If there is an obstacle on a tile, the robot cannot visit it. The robot moves to an adjacent tile with one move. The tile onto which the robot moves must be one of four tiles (i.e., east, west, north or south) adjacent to the tile where the robot is present. The robot may visit a tile twice or more.
Your task is to write a program which computes the minimum number of moves for the robot to change all 'dirty tiles' to 'clean tiles', if ever possible.
输入格式
IThe input consists of multiple maps, each representing the size and arrangement of the room. A map is given in the following format.
w h
c11 c12 c13 ... c1w
c21 c22 c23 ... c2w
...
ch1 ch2 ch3 ... chw
The integers w and h are the lengths of the two sides of the floor of the room in terms of widths of floor tiles. w and h are less than or equal to 20. The character cyx represents what is initially on the tile with coordinates (x, y) as follows.
'.' : a clean tile
'*' : a dirty tile
'x' : a piece of furniture (obstacle)
'o' : the robot (initial position)
In the map the number of 'dirty tiles' does not exceed 10. There is only one 'robot'.
The end of the input is indicated by a line containing two zeros.
输出格式
For each map, your program should output a line containing the minimum number of moves. If the map includes 'dirty tiles' which the robot cannot reach, your program should output -1.
输入输出样例
输入 #1
7 5
.......
.o...*.
.......
.*...*.
.......
15 13
.......x.......
...o...x....*..
.......x.......
.......x.......
.......x.......
...............
xxxxx.....xxxxx
...............
.......x.......
.......x.......
.......x.......
..*....x....*..
.......x.......
10 10
..........
..o.......
..........
..........
..........
.....xxxxx
.....x....
.....x.*..
.....x....
.....x....
0 0
输出 #1
8
49
-1
题解:
状压DP(简化TSP问题) + BFS求最短路径
题目大意:
m*n矩阵,有不超过10个需要走到的点,给出起点,以及一些障碍,问走最少的步子把所有点走完。n、m <= 20
对于整道题来讲,看到给定的一张图,首先我们能够想到的就是用BFS(广度优先搜索)找出任意两个可行点(对于起点和所有要到达的‘*’点)之间的距离,那么我们就对每一个可行点跑一遍BFS将\(dis[][]\)数组预处理出来,最终按照简化的TSP问题,状态压缩求解即可。
对于这道题有以下需要注意的:
1.BFS的过程是否正确(可以手推)
2.对于状态压缩的状态更新是否正确
3.由于计算\(dp[][]\)时候可能涉及到两个\(inf\)相加而出现爆\(int\)的情况,所以对于\(ans,dp[][],dis[][]\)我们需要开\(long long\)
AC代码
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f;
int n, m, cnt;
char str[25][25];
ll dis[25][25], dp[5050][25], vis[25][25];
int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
struct Point{
int x, y;
}point[25];
void bfs(int s)
{
queue <Point> q;
Point cur, next;
cur.x = point[s].x;
cur.y = point[s].y;
memset(vis, -1, sizeof vis);
vis[cur.x][cur.y] = 0;
q.push(cur);
while (!q.empty())
{
cur = q.front();
q.pop();
for (int i = 0; i < 4; i ++ )
{
next.x = cur.x + dir[i][0];
next.y = cur.y + dir[i][1];
if (next.x < 0 || next.y < 0 || next.x >= n || next.y >= m)
continue ;
if (str[next.x][next.y] == 'x')
continue ;
if (vis[next.x][next.y] == -1)
{
vis[next.x][next.y] = vis[cur.x][cur.y] + 1;
q.push(next);
char ch = str[next.x][next.y];
if (ch >= 'a' && ch < 'o')
dis[s][ch - 'a' + 1] = dis[ch -'a' + 1][s] = vis[next.x][next.y];
}
}
}
}
int main()
{
while(scanf("%d%d", &m, &n) != EOF)
{
if (n == 0 && m == 0)
break;
for (int i = 0; i < n; i ++ )
scanf("%s", str[i]);
cnt = 0;
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < m; j ++ )
{
if (str[i][j] == 'o')
{
point[0].x = i;
point[0].y = j;
}
else if (str[i][j] == '*')
{
cnt ++ ;
str[i][j] = 'a' + cnt - 1;
point[cnt].x = i;
point[cnt].y = j;
}
}
}
if (cnt == 0)
{
printf("0\n");
continue ;
}
memset(dis, 0x3f3f3f3f, sizeof dis);
/* for(int i=1;i<=cnt;i++ )
for (int j = 1; j <= cnt; j ++ )
dis[i][j] = inf;*/
for (int i = 0; i < cnt; i ++ )
{
bfs(i);
/*for (int j = 0; j <= cnt; j ++ )
{
if (dis[i][j] == -1)
dis[i][j] = inf;
}*/
}
/*cout << endl << endl;
for (int i = 1; i <= cnt; i ++ )
{
printf("%d ", dis[0][i]);
}
cout << endl;*/
//memset(dp, -1, sizeof dp);
//dp[0][0] = 0;
int tot = (1 << cnt) - 1;
for (int s = 0; s <= tot; s ++ )
{
for (int i = 1; i <= cnt; i ++ )
{
if (s & (1 << (i - 1)))
{
if (s == (1 << (i - 1)))
dp[s][i] = dis[0][i];
else
{
dp[s][i] = inf;
for (int j = 1; j <= cnt; j ++ )
{
if (s & (1 << (j - 1)) && i != j)
dp[s][i] = min(dp[s][i], dp[s ^ (1 << (i - 1))][j] + dis[j][i]);
}
}
}
}
}
ll ans = inf;
for (int i = 1; i <= cnt; i ++ )
ans = min(ans, dp[tot][i]);
if (ans >= 80000)
printf("-1\n");
else printf("%lld\n", ans);
}
return 0;
}