题意:

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

输入:

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

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

接下来输入矩阵。

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

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

输出:

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

吐槽:

我刚开始一看n和m是500,q是n*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年往回减阻碍,每减一个阻碍就将这个点与它周围所存在的通路合并,直到将s1和t1合并到同一个集合里。然后输出此时的年限即可。时间复杂度为O(n*m+q)。
 #include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int N = ; struct Node
{
int x, y;
}; int dis[][] = {{, }, {-, }, {, }, {, -}}; //搜索的方向 int t, n, m, q;
char mp[N][N]; //初始地图
char s[N][N]; //每次搜的地图
bool vis[N][N]; //记忆化剪枝
Node ss[N*N]; //记录(离线)生成出阻碍 bool bfs(int x) //bfs瞎jb搜,能隔开返回0,连通则返回1
{
for(int i = ; i < n; i++)
{
for(int j = ; j < m; j++) s[i][j] = mp[i][j];
}
for(int i = ; i < x; i++) s[ss[i].x][ss[i].y] = '';
queue<Node> que;
memset(vis, , sizeof(vis));
for(int i = ; i < m; i++)
{
if(s[][i] == '')
{
Node p;
p.x = ;
p.y = i;
que.push(p);
vis[][i] = ;
}
}
while(!que.empty())
{
Node p = que.front();
que.pop();
Node pp;
for(int i = ; i < ; i++)
{
int dx = p.x+dis[i][];
int dy = p.y+dis[i][];
if(dx >= && dx < n && dy >= && dy < m && s[dx][dy] == '' && vis[dx][dy] == )
{
if(dx == n-) return ;
pp.x = dx;
pp.y = dy;
que.push(pp);
vis[dx][dy] = ;
}
}
}
return ;
} void Init()
{
scanf("%d%d", &n, &m);
for(int i = ; i < n; i++)
{
scanf("%s", mp[i]);
}
scanf("%d", &q);
for(int i = ; i < q; i++) scanf("%d%d", &ss[i].x, &ss[i].y);
} void Work()
{
int l = , r = q-;
if(!bfs(l)) //判断初始能否隔开
{
printf("0\n");
return;
}
if(bfs(r)) //判断最终能否隔开
{
printf("-1\n");
return;
}
bool flag;
while(l < r-) //二分年限
{
int lr = (l+r)/;
flag = bfs(lr);
if(flag) l = lr;
else r = lr;
}
printf("%d\n", r);
} int main()
{
scanf("%d", &t);
for(int tm = ; tm <= t; tm++)
{
Init();
Work();
}
return ;
}

二分+bfs

 #include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std; const int N = ; int dis[][] = {{, }, {, -}, {, }, {-, }}; //四个方向搜索 int t;
int n, m;
int q;
char mp[N][N]; //地图
int s[N*N][]; //记录(离线)生成的阻碍
int fm[N*N]; //并查集
int vis[N][N]; //dfs记忆化 int mfind(int x) //并查集查询
{
int fx = x;
while(fx != fm[fx])
{
fx = fm[fx];
}
while(x != fx)
{
int mx = fm[x];
fm[x] = fx;
x = mx;
}
return fx;
} void mmerge(int x, int y) //并查集合并
{
int fx = mfind(x);
int fy = mfind(y);
if(fx != fy) fm[fy] = fx;
} void dfs(int x, int y) //遍历全图
{
int dx, dy;
for(int i = ; i < ; i++)
{
dx = x+dis[i][];
dy = y+dis[i][];
if(dx >= && dx < n && dy >= && dy < m && mp[dx][dy] == '' && !vis[dx][dy])
{
mmerge(x*m+y, dx*m+dy);
vis[dx][dy] = ;
dfs(dx, dy);
}
}
} void Init()
{
scanf("%d%d", &n, &m);
for(int i = ; i < n; i++) scanf("%s", mp[i]);
scanf("%d", &q);
for(int i = ; i < q; i++)
{
scanf("%d%d", &s[i][], &s[i][]);
mp[s[i][]][s[i][]] = '';
}
} void Work()
{ for(int i = ; i < n*m+; i++) fm[i] = i; //并查集预处理
memset(vis, , sizeof(vis)); //记忆化标记
for(int i = ; i < n; i++) //遍历全图,初始化并查集
{
for(int j = ; j < m; j++)
{
if(!vis[i][j] && mp[i][j] == '')
{
vis[i][j] = ;
dfs(i, j);
}
}
} for(int i = ; i < m; i++) //虚节点s1(n*m)与t1(n*m+1)的创建
{
mmerge(n*m, i);
mmerge(n*m+, (n-)*m+i);
}
if(mfind(n*m+) == mfind(n*m)) //始终无法隔开
{
printf("-1\n");
return;
} int p; //第p+1年隔开
bool flag = ; //临时标记,退出循环的判断
for(p = q-; p >= ; p--)
{
int dx, dy;
int xx = s[p][];
int yy = s[p][];
mp[xx][yy] = '';
for(int i = ; i < ; i++)
{
dx = xx+dis[i][];
dy = yy+dis[i][];
if(dx >= && dx < n && dy >= && dy < m && mp[dx][dy] == '')
{
mmerge(dx*m+dy, xx*m+yy);
if(mfind(n*m+) == mfind(n*m))
{
flag = ;
break;
}
}
}
if(flag) break;
}
if(flag) printf("%d\n", p+);
else printf("0\n");
} int main()
{
//freopen("test.in", "r", stdin);
scanf("%d", &t);
for(int tm = ; tm <= t; tm++)
{
Init();
Work();
}
}

并查集

hdu 5652 India and China Origins(二分+bfs || 并查集)BestCoder Round #77 (div.2)的更多相关文章

  1. HDU 5652 India and China Origins(经典并查集)

    特别经典的一个题,还有一种方法就是二分+bfs 题意:空间内n*m个点,每个点是0或者1,0代表此点可以走,1代表不能走.接着经过q年,每年一个坐标表示此点不能走.问哪年开始图上不能出现最上边不能到达 ...

  2. hdu 5652 India and China Origins 二分+bfs

    题目链接 给一个图, 由01组成, 1不能走. 给q个操作, 每个操作将一个点变为1, 问至少多少个操作之后, 图的上方和下方不联通. 二分操作, 然后bfs判联通就好了. #include < ...

  3. HDU 5652 India and China Origins 二分+并查集

    India and China Origins 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5652 Description A long time ...

  4. (hdu)5652 India and China Origins 二分+dfs

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5652 Problem Description A long time ago there ...

  5. HDU 5652 India and China Origins 二分优化+BFS剪枝

    题目大意:给你一个地图0代表可以通过1代表不可以通过.只要能从第一行走到最后一行,那么中国与印度是可以联通的.现在给你q个点,每年风沙会按顺序侵蚀这个点,使改点不可通过.问几年后中国与印度不连通.若一 ...

  6. hdu 5652 India and China Origins 并查集+二分

    India and China Origins Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/ ...

  7. HDU 5652 India and China Origins(并查集)

    India and China Origins Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/ ...

  8. hdu-5652 India and China Origins(二分+bfs判断连通)

    题目链接: India and China Origins Time Limit: 2000/2000 MS (Java/Others)     Memory Limit: 65536/65536 K ...

  9. 并查集(逆序处理):HDU 5652 India and China Origins

    India and China Origins Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/ ...

随机推荐

  1. 蓝桥杯 算法提高 8皇后·改 -- DFS 回溯

      算法提高 8皇后·改   时间限制:1.0s   内存限制:256.0MB      问题描述 规则同8皇后问题,但是棋盘上每格都有一个数字,要求八皇后所在格子数字之和最大. 输入格式 一个8*8 ...

  2. Javascript加速运动与减速运动

    加速运动,即一个物体运动时速度越来越快:减速运动,即一个物体运动时速度越来越慢.现在用Javascript来模拟这两个效果,原理就是用setInterval或setTimeout动态改变一个元素与另外 ...

  3. argunlar 1.0.0 【hello,world】

    <!DOCTYPE html><html lang="en" ng-app><head>    <meta charset="U ...

  4. continue和break区别

    break 语句用于跳出循环. continue 用于跳过循环中的一个迭代. 一个迭代,就是一次循环,continue终止本次循环,继续下一次循环: break,循环终止不再循环.

  5. 小程序登录、微信网页授权(Java版)

    首先呢,“登录”.“授权”.“授权登录”,是一样的意思,不用纠结. 写小程序授权登录的代码前,需要了解清楚openid与unionid的区别,这里再简单介绍一下: 腾讯有个 “微信·开放平台”,只有企 ...

  6. Anaconda+django写出第一个web app(八)

    今天来实现网站的登入和登出功能. 首先我们需要在urls.py中添加路径,注意此处的路径和在导航栏中设置的文字路径保持一致: from django.urls import path from . i ...

  7. Java Service Wrapper将java程序设置为服务

    有时候我们希望我们java写的程序作为服务注册到系统中,Java Service Wrapper(下面简称wrapper)是目前较为流行的将Java程序部署成Windows服务的解决方案, 本文将讨论 ...

  8. 【前端开发】关于闭包最通俗易懂的解释 for循环,定时器,闭包混合一块的那点事。

    for循环,定时器,闭包混合一块的那点事. 1,对于一个基本的for循环,顺序输出变量值. for(var i = 1; i < 4; i++){ console.log(i);//结果不多说了 ...

  9. jquery-实用例子

    一:jquery实现全选取消反选 3元运算:条件?真值:假值 <!DOCTYPE html> <html lang="en"> <head> & ...

  10. spirngboot 注解方式注入自定义参数

    在代码中 @value("oracle.user") private String user; 在配置文件中 oracle.user=root