Tunnels
Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2398 Accepted Submission(s): 728
Problem Description
Bob is travelling in Xi’an. He finds many secret tunnels beneath the city. In his eyes, the city is a grid. He can’t enter a grid with a barrier. In one minute, he can move into an adjacent grid with no barrier. Bob is full of curiosity
and he wants to visit all of the secret tunnels beneath the city. To travel in a tunnel, he has to walk to the entrance of the tunnel and go out from the exit after a fabulous visit. He can choose where he starts and he will travel each of the tunnels once
and only once. Now he wants to know, how long it will take him to visit all the tunnels (excluding the time when he is in the tunnels).
Input
The input contains mutiple testcases. Please process till EOF.
For each testcase, the first line contains two integers N (1 ≤ N ≤ 15), the side length of the square map and M (1 ≤ M ≤ 15), the number of tunnels.
The map of the city is given in the next N lines. Each line contains exactly N characters. Barrier is represented by “#” and empty grid is represented by “.”.
Then M lines follow. Each line consists of four integers x1, y1, x2, y2, indicating there is a tunnel with entrence in (x1, y1) and exit in (x2, y2). It’s guaranteed that (x1, y1) and (x2, y2) in the map are both empty grid.
For each testcase, the first line contains two integers N (1 ≤ N ≤ 15), the side length of the square map and M (1 ≤ M ≤ 15), the number of tunnels.
The map of the city is given in the next N lines. Each line contains exactly N characters. Barrier is represented by “#” and empty grid is represented by “.”.
Then M lines follow. Each line consists of four integers x1, y1, x2, y2, indicating there is a tunnel with entrence in (x1, y1) and exit in (x2, y2). It’s guaranteed that (x1, y1) and (x2, y2) in the map are both empty grid.
Output
For each case, output a integer indicating the minimal time Bob will use in total to walk between tunnels.
If it is impossible for Bob to visit all the tunnels, output -1.
If it is impossible for Bob to visit all the tunnels, output -1.
Sample Input
5 4 ....# ...#. ..... ..... ..... 2 3 1 4 1 2 3 5 2 3 3 1 5 4 2 1
Sample Output
7
题意:有一个图, ‘.’表示可以通行,'#'表示不能通行,然后给出 m 个管道的起始点和结束点
你可以从任意一个管道开始,要走过每一条管道,问最短时间花费为多少。
在管道里走无花费,在路面上走一格花费为一,只能上下左右走
思路:既然要遍历所有管道,且花费最少,那么就先得在管道之间建立起最短距离
于是我们枚举每个管道的结束为止,然后去枚举其他管道的开始为止,用 bfs 获取之间的最短距离,然后存下这个距离,构成一个新的图。
整幅图 最大不超过 15 * 15 ,管道最多也就是 15 个,可以用状态压缩动态规划就是状压DP写
dp[i][j] 中 i 表示当前站立的状态, j 表示当前所站的管道编号, 所以 i 范围是 0 到 1 << m , j范围是 0 到 m
(ps:i 是表示一个状态 例如 :i = 5 = 101 (2进制) 表示 第一和第三个位置已经访问过了)
然后给所有状态赋值为 inf ,但是 dp[1 << i][i] 需要赋值为 0 ,因为这表示当前站的点为起始点
所以最大状态就是 1 << m ,枚举 1 到 (1 << m) 的状态,在状态下枚举每个已访问过的点 j
(i & (1 << j)) = 0 就表示这个点在这个状态下是没访问过的,那就跳过,否则就枚举第二个点 k
(i & (1 << k)) = 0 表示 k 也没有访问过,也跳过,或者 j = k 也跳过,然后就是更新最小值了
从 k 去到 j ,在 i 状态下是 j k 都有访问过的,那么 i ^ (1 << j) 就表示 j 没有被访问过的状态,但是访问了 j 就到达当前 i 状态了,所以这里就可以表示 从 k 到 j ,看 k 到 j 的话值能不能更新小,就如此枚举完所有其他点,就能得到当前状态的最小值了。
那么要遍历完所有管道,就是 111111111111的情况, 就是枚举所有的 j使得 i & (1 << j) = 1的时候,就表示每个点都有访问过,然后去 dp[i] 下找最小的值就可以了!
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f
#define maxn 20
#define mem(a,x) memset(a,x,sizeof(a))
const int dir[4][2] = {1,0,-1,0,0,1,0,-1};
char mapp[maxn][maxn];
int n,m;
struct node1{
int x1,y1,x2,y2;
}a[maxn];
struct node2{
int x,y,dis;
};
int b[maxn][maxn][maxn][maxn];
int ex,ey;
bool vis[maxn][maxn];
int dp[33000][maxn];
int bfs(int sx,int sy){
mem(vis,false);
queue<node2>q;
node2 s,e;
s.x = sx;
s.y = sy;
s.dis = 0;
q.push(s);
vis[sx][sy] = true;
while(q.size()){
s = q.front();
q.pop();
if(s.x == ex && s.y == ey){
return s.dis;
}
for(int i = 0;i < 4;i++){
int mx = s.x + dir[i][0];
int my = s.y + dir[i][1];
if(mx < 1 || mx > n || my < 1 || my > n || vis[mx][my] || mapp[mx][my] == '#')
continue;
vis[mx][my] = true;
e.x = mx;
e.y = my;
e.dis = s.dis + 1;
q.push(e);
}
}
return inf;
}
int main(){
while(scanf("%d %d",&n,&m) != EOF){
for(int i = 1;i <= n;i++)
scanf("%s",mapp[i] + 1);
for(int i = 0;i < m;i++){
scanf("%d %d %d %d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
}
mem(b,inf);
for(int i = 0;i < m;i++){
for(int j = 0;j < m;j++){
if(i == j)
continue;
ex = a[j].x1,ey = a[j].y1;
int dis = bfs(a[i].x2,a[i].y2);
b[a[i].x2][a[i].y2][a[j].x1][a[j].y1] = dis;
}
}
int N = 1 << m;
for(int i = 0;i < N;i++){
for(int j = 0;j < m;j++)
dp[i][j] = inf;
}
for(int i = 0;i < m;i++)
dp[(1 << i)][i] = 0; // 起始点
int ans = inf;
for(int i = 1;i < N;i++){
int flag = 1;
for(int j = 0;j < m;j++){
if(!(i & (1 << j))){
flag = 0;// 每个管道都去过了才去计算 ans 值
continue;
}
for(int k = 0;k < m;k++){
if(!(i & (1 << k)) || j == k) // 第k个管道已去过则取值
continue;
dp[i][j] = min(dp[i][j],dp[i ^ (1 << j)][k] + b[a[k].x2][a[k].y2][a[j].x1][a[j].y1]);
// 到达 j 点的时候,是 状态 i, 从前一个状态算,找到到达 j 且状态为 i 时的最短路
}
}
if(flag){
for(int j = 0;j < m;j++)
ans = min(ans,dp[i][j]);
}
}
if(ans == inf)
puts("-1");
else
printf("%d\n",ans);
}
return 0;
}