hdu 5652 India and China Origins(二分+bfs || 并查集)BestCoder Round #77 (div.2)

题意:

给一个n*m的矩阵作为地图,0为通路,1为阻碍。只能向上下左右四个方向走。每一年会在一个通路上长出一个阻碍,求第几年最上面一行与最下面一行会被隔开。

输入:

首行一个整数t,表示共有t组数据。

每组数据首行两个整数n, m,表示矩阵大小。

接下来输入矩阵。

接下来输入一个整数q,表示一共q年。

接下来q行,第i行为两个整数xi, yi,表示在第i年会在xi, yi长出一个阻碍。数据保证只会在通路上生成阻碍。

输出:

如果在第i年被隔开,则输出i。如果始终无法隔开,则输出-1

吐槽:

我刚开始一看nm500qn*m,然后判断数据范围是500^3,然后果断判断暴力bfs能过,直接开干,小数据还真过了T_T,结果终测跪了Orz。后来发现其实数据范围是500^4_→,也就是n*m*q。觉得二分+bfs能过。结果别人说确实能过。然后我自己写了一下,顺利过了。但二分还是写的不熟,中间出了点小差错=_=

看题解标程居然是并查集,果断给跪,这么吊的思路也是没谁了,而且实现起来也是非常简单的并查集。直接套模板都可以的那种。亏我还专门搞过一段时间的并查集呢,思路还是不够宽广啊,以后脑洞要更大一点才行。嗯,就这么愉快地决定了。

题解:

  1. 二分+bfs。很好理解,将第一行入队,然后bfs向上下左右瞎jb搜,只要能够搜到最后一行,就表示没有隔开(反之亦然)。二分年限即可。时间复杂度O(n*m*log2(q))
  2. 离线并查集,将所有的q保存下来,然后生成最终地图。然后建立初始并查集,将每个通路和它上下左右四个通路连接,创建两个虚节点s1, t1,将第一行所有点连接到s1,将最后一行所有点连接到t1。然后从第q年往回减阻碍,每减一个阻碍就将这个点与它周围所存在的通路合并,直到将s1t1合并到同一个集合里。然后输出此时的年限即可。时间复杂度为O(n*m+q)
  1 #include <cstdio>
  2 #include <cmath>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <queue>
  6 using namespace std;
  7 
  8 const int N = 510;
  9 
 10 struct Node
 11 {
 12     int x, y;
 13 };
 14 
 15 int dis[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};             //搜索的方向
 16 
 17 int t, n, m, q;
 18 char mp[N][N];                                                  //初始地图
 19 char s[N][N];                                                   //每次搜的地图
 20 bool vis[N][N];                                                 //记忆化剪枝
 21 Node ss[N*N];                                                   //记录(离线)生成出阻碍
 22 
 23 bool bfs(int x)                                                 //bfs瞎jb搜,能隔开返回0,连通则返回1
 24 {
 25     for(int i = 0; i < n; i++)
 26     {
 27         for(int j = 0; j < m; j++) s[i][j] = mp[i][j];
 28     }
 29     for(int i = 0; i < x; i++) s[ss[i].x][ss[i].y] = '1';
 30     queue<Node> que;
 31     memset(vis, 0, sizeof(vis));
 32     for(int i = 0; i < m; i++)
 33     {
 34         if(s[0][i] == '0')
 35         {
 36             Node p;
 37             p.x = 0;
 38             p.y = i;
 39             que.push(p);
 40             vis[0][i] = 1;
 41         }
 42     }
 43     while(!que.empty())
 44     {
 45         Node p = que.front();
 46         que.pop();
 47         Node pp;
 48         for(int i = 0; i < 4; i++)
 49         {
 50             int dx = p.x+dis[i][0];
 51             int dy = p.y+dis[i][1];
 52             if(dx >= 0 && dx < n && dy >= 0 && dy < m && s[dx][dy] == '0' && vis[dx][dy] == 0)
 53             {
 54                 if(dx == n-1) return 1;
 55                 pp.x = dx;
 56                 pp.y = dy;
 57                 que.push(pp);
 58                 vis[dx][dy] = 1;
 59             }
 60         }
 61     }
 62     return 0;
 63 }
 64 
 65 void Init()
 66 {
 67     scanf("%d%d", &n, &m);
 68     for(int i = 0; i < n; i++)
 69     {
 70         scanf("%s", mp[i]);
 71     }
 72     scanf("%d", &q);
 73     for(int i = 0; i < q; i++) scanf("%d%d", &ss[i].x, &ss[i].y);
 74 }
 75 
 76 void Work()
 77 {
 78     int l = 0, r = q-1;
 79     if(!bfs(l))                                                         //判断初始能否隔开
 80     {
 81         printf("0
");
 82         return;
 83     }
 84     if(bfs(r))                                                          //判断最终能否隔开
 85     {
 86         printf("-1
");
 87         return;
 88     }
 89     bool flag;
 90     while(l < r-1)                                                      //二分年限
 91     {
 92         int lr = (l+r)/2;
 93         flag = bfs(lr);
 94         if(flag) l = lr;
 95         else r = lr;
 96     }
 97     printf("%d
", r);
 98 }
 99 
100 int main()
101 {
102     scanf("%d", &t);
103     for(int tm = 1; tm <= t; tm++)
104     {
105         Init();
106         Work();
107     }
108     return 0;
109 }
二分+bfs
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 const int N = 510;
  8 
  9 int dis[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; //四个方向搜索
 10 
 11 int t;
 12 int n, m;
 13 int q;
 14 char mp[N][N];                                      //地图
 15 int s[N*N][2];                                      //记录(离线)生成的阻碍
 16 int fm[N*N];                                        //并查集
 17 int vis[N][N];                                      //dfs记忆化
 18 
 19 int mfind(int x)                                    //并查集查询
 20 {
 21     int fx = x;
 22     while(fx != fm[fx])
 23     {
 24         fx = fm[fx];
 25     }
 26     while(x != fx)
 27     {
 28         int mx = fm[x];
 29         fm[x] = fx;
 30         x = mx;
 31     }
 32     return fx;
 33 }
 34 
 35 void mmerge(int x, int y)                           //并查集合并
 36 {
 37     int fx = mfind(x);
 38     int fy = mfind(y);
 39     if(fx != fy) fm[fy] = fx;
 40 }
 41 
 42 void dfs(int x, int y)                              //遍历全图
 43 {
 44     int dx, dy;
 45     for(int i = 0; i < 4; i++)
 46     {
 47         dx = x+dis[i][0];
 48         dy = y+dis[i][1];
 49         if(dx >= 0 && dx < n && dy >= 0 && dy < m && mp[dx][dy] == '0' && !vis[dx][dy])
 50         {
 51             mmerge(x*m+y, dx*m+dy);
 52             vis[dx][dy] = 1;
 53             dfs(dx, dy);
 54         }
 55     }
 56 }
 57 
 58 void Init()
 59 {
 60     scanf("%d%d", &n, &m);
 61     for(int i = 0; i < n; i++) scanf("%s", mp[i]);
 62     scanf("%d", &q);
 63     for(int i = 0; i < q; i++)
 64     {
 65         scanf("%d%d", &s[i][0], &s[i][1]);
 66         mp[s[i][0]][s[i][1]] = '1';
 67     }
 68 }
 69 
 70 void Work()
 71 {
 72 
 73 
 74     for(int i = 0; i < n*m+2; i++) fm[i] = i;       //并查集预处理
 75     memset(vis, 0, sizeof(vis));                    //记忆化标记
 76     for(int i = 0; i < n; i++)                      //遍历全图,初始化并查集
 77     {
 78         for(int j = 0; j < m; j++)
 79         {
 80             if(!vis[i][j] && mp[i][j] == '0')
 81             {
 82                 vis[i][j] = 1;
 83                 dfs(i, j);
 84             }
 85         }
 86     }
 87 
 88     for(int i = 0; i < m; i++)                      //虚节点s1(n*m)与t1(n*m+1)的创建
 89     {
 90         mmerge(n*m, i);
 91         mmerge(n*m+1, (n-1)*m+i);
 92     }
 93     if(mfind(n*m+1) == mfind(n*m))                  //始终无法隔开
 94     {
 95         printf("-1
");
 96         return;
 97     }
 98 
 99     int p;                                          //第p+1年隔开
100     bool flag = 0;                                  //临时标记,退出循环的判断
101     for(p = q-1; p >= 0; p--)
102     {
103         int dx, dy;
104         int xx = s[p][0];
105         int yy = s[p][1];
106         mp[xx][yy] = '0';
107         for(int i = 0; i < 4; i++)
108         {
109             dx = xx+dis[i][0];
110             dy = yy+dis[i][1];
111             if(dx >= 0 && dx < n && dy >= 0 && dy < m && mp[dx][dy] == '0')
112             {
113                 mmerge(dx*m+dy, xx*m+yy);
114                 if(mfind(n*m+1) == mfind(n*m))
115                 {
116                     flag = 1;
117                     break;
118                 }
119             }
120         }
121         if(flag) break;
122     }
123     if(flag) printf("%d
", p+1);
124     else printf("0
");
125 }
126 
127 int main()
128 {
129     //freopen("test.in", "r", stdin);
130     scanf("%d", &t);
131     for(int tm = 1; tm <= t; tm++)
132     {
133         Init();
134         Work();
135     }
136 }
并查集
原文地址:https://www.cnblogs.com/mypride/p/5343013.html