题意——
一个n*m的地图,从左上角走到右下角。
这个地图是一个01串,要求我们行走的路径形成的01串最小。
注意,串中最左端的0全部可以忽略,除非是一个0串,此时输出0。
例:
3 3
001
110
001
此图的最短路径为101。
输入——
第一行输入一个整数t,表示共有t组数据。
接下来每组第一行输入两个整数n, m。表示地图的长和宽。
接下来n行,每行m个字符。字符只有0或1。
输出——
输出一个字符串,表示最短路径。
这道题刚开始用了优先队列+大数写的bfs,然后无限爆tle,后来想了想——
首先,用优先队列时,每个节点入队的时间复杂度为log2(k),其中k为当前队列的长度。
其次,大数每次乘2的操作都要len次,每次在入队时和其他节点比较时还可能要len次,len为数的长度。
而地图最大是1000*1000的,总共的时间复杂度粗略算一下要10^6*log2(10^3)*40,刚好卡在时限上了,再乘上一个常数,就超时了,再乘上t,更超时了。
不能用优先队列,用普通队列,怎么写想了好久,在比赛结束之后又看了一下题解,发现大概有一下几种解法——
- 先搜出来从起点出发的所有0,此时起点必须是0,用bfs向4个方向搜;然后从所有0出发,寻找到终点的点,用bfs向下或向右走;然后再从终点出发,往回走,用dfs;然后就看不懂了……
- 先搜出来从起点出发的所有0,此时起点必须是0,用bfs向4个方向搜;然后在这些0中选取行号和列号相加最大的点,因为这些点距离终点最近,可以得知,这些点在同一斜行,然后从这些点所在的斜行的下一斜行开始,一行一行向下进行递推——优先选取0,对本斜行中选取的字符标记,然后对下一斜行进行递推时,只考虑可以从标记点走到的点。
当然,当对0进行搜索时,已经搜到终点,那么输出0就可以了。
我用的是第二种方法。
耗时109ms,内存4520k。
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 #include <queue> 6 using namespace std; 7 8 const int N = 1010; 9 10 struct Node 11 { 12 int x, y; 13 }; 14 15 int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; 16 int sxy[N*N]; 17 char mp[N][N]; 18 bool vis[N][N]; 19 int n, m, t, rt, k; 20 queue<Node> q0; 21 22 int bfs0() 23 { 24 Node p, ps; 25 p.x = p.y = 0; 26 vis[0][0] = 1; 27 if(mp[0][0] == '0') q0.push(p); 28 else 29 { 30 sxy[0] = 0; 31 k = 1; 32 printf("1"); 33 return 0; 34 } 35 k = 0; 36 while(!q0.empty()) 37 { 38 p = q0.front(); 39 q0.pop(); 40 sxy[k++] = p.x+p.y; 41 if(p.x == n-1 && p.y == m-1) return 1; 42 for(int i = 0; i < 4; i++) 43 { 44 int sx = p.x+dir[i][0]; 45 int sy = p.y+dir[i][1]; 46 if(sx >= 0 && sx < n && sy >= 0 && sy < m && !vis[sx][sy] && mp[sx][sy] == '0') 47 { 48 vis[sx][sy] = 1; 49 ps.x = sx; 50 ps.y = sy; 51 q0.push(ps); 52 } 53 } 54 } 55 return 0; 56 } 57 58 void sroot() 59 { 60 rt = 0; 61 for(int i = 0; i < k; i++) if(rt < sxy[i]) rt = sxy[i]; 62 } 63 64 void getans() 65 { 66 for(int i = rt+1; i <= m+n-2; i++) 67 { 68 k = 0; 69 if(i >= n-1) k = i-n+1; 70 int tmp = 2; 71 for(int j = k; j < m && j <= i; j++) 72 { 73 if((i-j-1 >= 0 && i-j < n && j >= 0 && vis[i-j-1][j]) 74 || (i-j >= 0 && i-j < n && j-1 >= 0 && vis[i-j][j-1])) 75 { 76 int mid = mp[i-j][j]-'0'; 77 tmp = tmp < mid ? tmp : mid; 78 } 79 } 80 printf("%d", tmp); 81 for(int j = k; j < m && j <= i; j++) 82 { 83 if((i-j-1 >= 0 && i-j < n && j >= 0 && vis[i-j-1][j]) 84 || (i-j >= 0 && i-j < n && j-1 >= 0 && vis[i-j][j-1])) 85 { 86 if(mp[i-j][j]-'0' == tmp) vis[i-j][j] = 1; 87 } 88 } 89 } 90 printf(" "); 91 } 92 93 void init() 94 { 95 scanf("%d%d", &n, &m); 96 for(int i = 0; i < n; i++) scanf("%s", mp[i]); 97 memset(vis, 0, sizeof(vis)); 98 while(!q0.empty()) q0.pop(); 99 } 100 101 int main() 102 { 103 //freopen("test.in", "r", stdin); 104 //freopen("test.out", "w", stdout); 105 scanf("%d", &t); 106 while(t--) 107 { 108 init(); 109 if(bfs0()) printf("0 "); 110 else 111 { 112 sroot(); 113 getans(); 114 } 115 } 116 return 0; 117 }