hdu 5335 Walk Out(bfs+斜行递推) 2015 Multi-University Training Contest 4

题意——

一个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,更超时了。

 

不能用优先队列,用普通队列,怎么写想了好久,在比赛结束之后又看了一下题解,发现大概有一下几种解法——

  1. 先搜出来从起点出发的所有0,此时起点必须是0,用bfs向4个方向搜;然后从所有0出发,寻找到终点的点,用bfs向下或向右走;然后再从终点出发,往回走,用dfs;然后就看不懂了……
  2. 先搜出来从起点出发的所有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("\n");
 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\n");
110         else
111         {
112             sroot();
113             getans();
114         }
115     }
116     return 0;
117 }
View Code

 

转载于:https://www.cnblogs.com/mypride/p/4693712.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值