Tunnels
Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 90 Accepted Submission(s): 28
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.
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.
Sample Input
5 4 ....# ...#. ..... ..... ..... 2 3 1 4 1 2 3 5 2 3 3 1 5 4 2 1
Sample Output
Source
2014西安全国邀请赛
传送门:【HDU】4856 Tunnels
题目大意:一个边长为n的正方形网格图,其中有一些点' . '表示可达,' # '表示不可达,你不能走到不可达的点上,以及每一个单位时间你只能走到相邻的网格(上下左右)。现在给你m条密道,每条密道起始位置(x1,y1),终点位置(x2,y2),当你从起点进去后能瞬间从终点位置出来(不花时间),但是每条密道你只能走一遍。现在,你可以选择任意一个可达的点作为起点,问能否在满足条件下走完所有的密道,有解输出最短时间,否则输出-1。
题目分析:先BFS预处理出每个密道的终点 i 到所有走路能到达的起点 j 的距离dist[ i ][ j ],不能到达设为INF。
然后就是简单的状压DP了。。。
设dp[ i ][ j ]表示已经经过的密道状态为 i (i为压缩状态,二进制的每一位表示相应的密道是否已经走过,走过为1,否则为0),最后一个经过的密道是j时的最小用时。
则状态转移方程为:dp[ i | ( 1 << k ) ][ k ] = min { dp[ i | ( 1 << k ) ][ k ] , dp[ i ][ j ] + dist[ j ][ k ] } 。
其中必须保证dp[ i ][ j ]已经有了的状态,dist[ j ][ k ]不是INF ( j 出发能到达 k ),以及状态 i 中不包含第 k 个密道能才发生转移。
PS:比赛的时候脑洞太小,竟然往最短路上敲,然后wa的生活不能自理。。。赛后果断换思路,状压DP。其实比赛的时候还没敲代码的时候我就想到了的,和以前做过的一道AC自动机的转移很像啊,不过被队友神奇的时间复杂度分析踢开了,正好我当时脑子晕忽忽的,也就再也没往这方面想了= =||,早知道就敲了。。。。
代码如下:
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 16 ;
const int MAXQ = 1005 ;
const int INF = 0x3f3f3f3f ;
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define clear( a , x ) memset ( a , x , sizeof a )
struct queue {
int x , y ;
queue ( int X = 0 , int Y = 0 ) : x ( X ) , y ( Y ) {}
} ;
queue Q[MAXQ] ;
int head , tail ;
char G[MAXN][MAXN] ;
int sx[MAXN] , sy[MAXN] , ex[MAXN] , ey[MAXN] ;
int d[MAXN][MAXN] ;
int dist[MAXN][MAXN] ;
int path[4][2] = { { 1 , 0 } , { -1 , 0 } , { 0 , 1 } , { 0 , -1 } } ;
bool vis[MAXN][MAXN] ;
int dp[1 << 15][15] ;
int n , m ;
void bfs ( int idx ) {
int x , y , nx , ny ;
queue u ;
clear ( vis , 0 ) ;
head = tail = 0 ;
vis[ex[idx]][ey[idx]] = 1 ;
d[ex[idx]][ey[idx]] = 0 ;
Q[tail ++] = queue ( ex[idx] , ey[idx] ) ;
while ( head != tail ) {
u = Q[head ++] ;
REP ( k , 4 ) {
nx = u.x + path[k][0] ;
ny = u.y + path[k][1] ;
if ( G[nx][ny] == '.' && !vis[nx][ny] ) {
vis[nx][ny] = 1 ;
Q[tail ++] = queue ( nx , ny ) ;
d[nx][ny] = d[u.x][u.y] + 1 ;
}
}
}
REP ( i , m )
if ( vis[sx[i]][sy[i]] )
dist[idx][i] = d[sx[i]][sy[i]] ;
dist[idx][idx] = 0 ;
}
void work () {
while ( ~scanf ( "%d%d" , &n , &m ) ) {
clear ( dist , INF ) ;
REP ( i , 1 << m )
REP ( j , m )
dp[i][j] = INF ;
clear ( G , '#' ) ;
REPF ( i , 1 , n )
scanf ( "%s" , &G[i][1] ) ;
REP ( i , m )
scanf ( "%d%d%d%d" , &sx[i] , &sy[i] , &ex[i] , &ey[i] ) ;
REP ( i , m )
bfs ( i ) ;
REP ( i , n )
dp[1 << i][i] = 0 ;
int o = 1 << m ;
REP ( i , o )
REP ( j , m )
if ( dp[i][j] != INF )
REP ( k , m )
if ( dist[j][k] != INF && !( i & ( 1 << k ) ) ) {
int nxt = i | ( 1 << k ) ;
dp[nxt][k] = min ( dp[nxt][k] , dp[i][j] + dist[j][k] ) ;
}
int ans = INF ;
REP ( i , m )
if ( ans > dp[o - 1][i] )
ans = dp[o - 1][i] ;
if ( ans == INF )
ans = -1 ;
printf ( "%d\n" , ans ) ;
}
}
int main () {
work () ;
return 0 ;
}
最后附上代码生成器以及几组随机数据:
/*
//11组数据
10 12
#.##.#.###
#.#.##..##
.######...
...####.##
##.#..##..
.#..#.##..
.#.##.###.
##.##....#
###.##.###
.####....#
5 3 3 8
8 6 8 3
4 2 8 3
3 1 5 6
3 1 4 8
5 3 3 1
4 1 4 8
3 8 6 10
4 3 8 9
8 9 3 8
10 6 5 3
9 4 10 7
6 10
##.##.
.#.###
##..#.
.#..#.
.####.
##....
3 6 4 4
5 6 5 1
4 6 4 3
4 3 4 3
4 6 5 1
5 1 1 3
5 1 4 3
4 4 6 3
6 6 3 4
6 3 6 3
14 13
#..#.###..#.##
....##....#.#.
..#.#..##.#.##
##...###...#.#
...##..#.##..#
##.###.####...
##....#.#.#.##
#..#.#########
#..##...#..#..
#..###.##.##.#
..#.....#####.
..#.##.####.##
..##.#.###.#..
...##..#.###..
4 5 8 5
7 10 1 5
7 5 1 5
5 6 2 8
3 6 2 9
7 8 9 8
9 6 8 5
2 2 14 6
9 8 4 4
11 1 13 5
14 14 1 2
9 14 5 13
10 13 7 4
9 7
.#..#.#.#
...#.###.
##.###..#
##.#..#.#
....###.#
..####.##
...#.#.#.
####.####
#..##.##.
5 3 6 1
5 2 3 8
4 5 7 1
6 7 2 9
9 9 6 1
7 2 2 5
4 5 9 2
6 14
####..
..##..
#..###
#.#...
.##.#.
##.###
1 6 1 5
4 2 4 2
5 1 3 3
5 4 2 2
4 2 3 3
4 4 5 1
3 2 3 3
4 5 1 5
4 6 4 5
2 6 1 5
1 6 1 5
3 3 5 6
1 6 2 1
2 2 4 2
15 3
..###.##..##.#.
..####......##.
..#..##.######.
#.##.###.#.#.#.
#.##.#.....####
.#..##..##...#.
##.####..#..#..
##.....##.#.#..
#...##.#.###.#.
##...#.##....##
####.####.#.##.
.##.#...###....
.#..##.###.....
#..##.#.##...##
.###.#..#..###.
12 14 10 11
7 9 9 15
1 1 9 4
2 12
..
##
1 1 1 2
1 2 1 1
1 1 1 2
1 1 1 2
1 2 1 1
1 2 1 2
1 1 1 2
1 2 1 2
1 2 1 1
1 2 1 2
1 2 1 2
1 1 1 1
12 10
.#.##..#...#
#.######..##
.##....#####
..#.....#..#
....#......#
##.##..#...#
.#..###.#.##
#.#.#.#.....
##..##...##.
.##.....#...
#.#.#..#####
...##...#.#.
10 7 5 6
5 4 12 10
10 1 12 10
8 10 3 4
1 3 6 7
4 6 7 10
10 11 4 4
11 6 6 10
11 2 1 7
4 1 5 2
12 7
.###..##..##
...##...##..
.#.#.#..#..#
#.#...###.##
#..#.#..##.#
.#...#..###.
.#...#....#.
..###.#...##
.#..#.#.###.
#...#.######
.#.#.####.##
.#..#.###.#.
8 9 4 5
3 7 12 10
3 3 6 8
7 5 10 6
4 10 3 5
12 10 3 10
10 4 11 3
6 2
..#..#
....#.
#.####
.##...
##.##.
#..#.#
1 1 5 6
6 2 5 3
6 14
#..#.#
......
#.#.##
..#.##
.#....
#..#..
1 5 4 4
6 6 2 3
3 4 6 5
6 3 2 2
2 6 1 2
5 3 2 3
2 4 6 6
4 1 6 6
1 5 1 2
2 2 5 5
2 6 6 3
5 3 5 1
5 3 5 3
5 4 2 5
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <time.h>
using namespace std ;
typedef long long LL ;
char g[20][20];
int main () {
freopen ( "test.txt" , "w" , stdout ) ;
int cas , n , m , i , x1 , y1 , x2 , y2 , cnt , tmp , j , flag ;
srand ( time ( NULL ) );
for ( cas = 1 ; cas <= 10 ; ++ cas ) {
n = rand () % 15 + 1 ;
m = rand () % 15 + 1 ;
printf ( "%d %d\n" , n , m ) ;
flag = 0 ;
while ( !flag ) {
for ( i = 1 ; i <= n ; ++ i ) {
cnt = 1;
while ( cnt <= n ) {
tmp = rand () % 2 ;
if ( tmp ) {
g[i][cnt ++] = '.' ;
flag = 1 ;
}
else
g[i][cnt ++] = '#' ;
}
}
}
for ( i = 1 ; i <= n ; ++ i ) {
for( j = 1 ; j <= n ; ++ j )
printf ( "%c" , g[i][j] ) ;
printf ( "\n" ) ;
}
for ( i = 1 ; i <= m ; i++ ) {
flag = 0 ;
while ( !flag ) {
x1 = rand () % n + 1 ;
y1 = rand () % n + 1 ;
x2 = rand () % n + 1 ;
y2 = rand () % n + 1 ;
if ( g[x1][y1] == '.' && g[x2][y2] == '.' )
flag = 1 ;
}
printf ( "%d %d %d %d\n" , x1 , y1 , x2 , y2 );
}
printf ( "\n" ) ;
}
return 0 ;
}