hdu-5652 并查集或者二分BFS
来源:互联网 发布:网络男神 毒药 编辑:程序博客网 时间:2024/06/04 18:28
这道题是我当时打BC的时候做到的,一点思路没有好不好,谁知道竟然是并查集(并查集是我学的第一个数据结构,学得最好),真是一口老血。同时自己也反思一下,一段时间没有碰一种算法,那么这种算法的灵敏度是不是退步的很厉害。打比赛的时候不会做,自然窥了一下Acfun的屏,那些巨巨在几秒种之内想到了三种方法,真是厉害啊=、=(膜)。那么,言归正传,我这里也来介绍一下这三种做法。
TIP:这道题是一道连通性测试问题,但是求连通的是两条边上的所有点,直接上的话非常麻烦。这里有个小套路,自己创造两个点(印度和中国,沙漠和海洋),将两个点与两条边上的点连通,那么我们测试的就是我们自己创造的两个点的连通性,而不必麻烦的测试两条边上所有点的连通性了,是不是简单很多!
PS:我要是知道有这个套路的话,那么这就是一道纯的连通性问题,还是比较好想到并查集的。唉!可惜,我是后来知道的。嗯,学到了新套路就是好事!
做法一 :二分+BFS
分析:不过如果你不知道前面的套路的话,应该会想到使用BFS的(迷宫问题变种?)。但是Q的值有点大,一个一个BFS肯定会TLE,但是BFS的结果满足二分的111100000的形式,可以使用二分,用logN的复杂度寻找正好转换的那个点。唉,比赛的时候没有想到,而且这类题目做的少,朋友给了我一句名言:题目不会做,不如试试二分。
#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 500+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;const int inf = 0x3f3f3f3f;typedef long long ll;const double pi = acos( -1 );const ll mod = 1e9+7;int n,m;char gra[maxn][maxn];char tmp[maxn][maxn];int vis[maxn][maxn];int qry[(maxn)*(maxn)][2];int dir[4][2]={0,1,0,-1,1,0,-1,0};bool bfs(int q){ for(int i=0;i<n;i++) for(int j=0;j<m;j++) tmp[i][j]=gra[i][j]; for(int i=0;i<=q;i++) tmp[qry[i][0]][qry[i][1]]='1'; clr(vis,0); queue<int>que; for(int i=0;i<m;i++) if(tmp[0][i]=='0') { vis[0][i]=1; que.push(i); } while(!que.empty()) { int a=que.front(); que.pop(); int b=a%1000; a/=1000; if(a==n-1)return true; for(int i=0;i<4;i++) { int ta=a+dir[i][0]; int tb=b+dir[i][1]; if(ta>=0&&ta<n&&tb>=0&&tb<m&&tmp[ta][tb]=='0'&&vis[ta][tb]==0) { vis[ta][tb]=1; que.push(ta*1000+tb); } } } return false;}int main(){ //freopen("d:\\acm\\in.in","r",stdin); int t; scanf("%d",&t); while(t--) { scanf("%d %d",&n,&m); for(int i=0;i<n;i++) scanf("%s",gra[i]); int q; scanf("%d",&q); for(int i=0;i<q;i++) scanf("%d %d",&qry[i][0],&qry[i][1]); int l=0,r=q; int mid; int ans=-1; while(l<=r) { mid=(l+r)>>1; if(!bfs(mid)) { ans=mid; r=mid-1; } else l=mid+1; } printf("%d\n",ans+1); } return 0;}
分析:这种做法是BC题解给出的做法,但是我觉得不是特别好,没有第三种做法好。
这次需要用到套路。从后往前看,将要变的山峰一次性先全部在图上表示出来(修改好),将图中可以走的格子(上下左右)作为边来看,这样方块图就是一个无向图,那么就是时时检测自己创造的两个点的连通性。按倒序将之前变化为山峰的点还原为平原,并且更新四周的边(注意如果在两条边上的话,还需要更新它和创造的两个点的连接),并且测试自己创造的两个点是不是在同一个集合里面(是否连通),找到第一连通的点,输入后一个的数值!
<span style="font-size:18px;">#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 500+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;const int inf = 0x3f3f3f3f;typedef long long ll;const double pi = acos( -1 );const ll mod = 1e9+7;int n,m,S,T;char gra[maxn][maxn];int pre[(maxn)*(maxn)];int qry[(maxn)*(maxn)][2];int dir[4][2]={0,1,0,-1,1,0,-1,0};int find(int x){ return pre[x]==x?x:pre[x]=find(pre[x]);}void un(int a,int b){ pre[find(a)]=find(b);}int code(int a,int b){ return a*m+b;}bool in(int a,int b){ if(a>=0&&a<n&&b>=0&&b<m)return true; return false;}void charu(int a,int b){ if(gra[a][b]=='1')return; for(int i=0;i<4;i++) { int ta=a+dir[i][0]; int tb=b+dir[i][1]; if(in(ta,tb)&&gra[ta][tb]=='0') un(code(a,b),code(ta,tb)); } if(a==0)un(code(a,b),S); if(a==n-1)un(code(a,b),T);}int main(){ //freopen("d:\\acm\\in.in","r",stdin); int t; scanf("%d",&t); while(t--) { scanf("%d %d",&n,&m); for(int i=0;i<n;i++) scanf("%s",gra[i]); S=n*m,T=S+1; for(int i=0;i<=T;i++) pre[i]=i; int q; scanf("%d",&q); for(int i=0;i<q;i++) { scanf("%d %d",&qry[i][0],&qry[i][1]); gra[qry[i][0]][qry[i][1]]='1'; } for(int i=0;i<n;i++) for(int j=0;j<m;j++) charu(i,j); if(find(S)==find(T)) { puts("-1"); continue; } for(int i=q-1;i>=0;i--) { gra[qry[i][0]][qry[i][1]]='0'; charu(qry[i][0],qry[i][1]); if(find(S)==find(T)) { printf("%d\n",i+1); break; } } } return 0;}</span>
分析:感觉这种做法才是正统做法,最快,最好!
同样需要用到套路,但是这次是将沙漠和山峰作为点(不是中国和印度)。而且这次连接的不是平原的点,而是山峰的点。如果山峰的点在侧面上能够连通的话,这就表明两个国家之间已经不能连通了。但是有一个注意点,山峰之间四面八方都算是连通的(至于为什么自己画画图就知道了),所以和上一种做法不一样的地方就在这里。然后按正序将平原的点不断变成山峰,更新与之相连的山峰点(同样如果是在两条边上的话,那么就需要与自己创造的两点进行连接),不断测试沙漠与海洋的连通性,找到第一个连通的点,输出即可!
#include<set>#include<map>#include<cmath>#include<stack>#include<queue>#include<vector>#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cctype>#define maxn 500+5#define clr(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const int inf = 0x3f3f3f3f;const double pi = acos( -1 );const ll mod = 1e9+7;const double eps = 1e-10;int S,T;int n,m;int pre[(maxn)*(maxn)];char gra[maxn][maxn];int dir[8][2]={0,1,0,-1,1,0,-1,0,1,-1,1,1,-1,1,-1,-1};int find(int x){ return pre[x]==x?x:pre[x]=find(pre[x]);}void un(int a,int b){ pre[find(a)]=find(b);}bool bing(int a,int b){ if(find(a)==find(b))return true; return false;}bool in(int a,int b){ if(a>=0&&a<n&&b>=0&&b<m)return true; return false;}int code(int a,int b){ return a*m+b;}void add(int a,int b){ for(int i=0;i<8;i++) { int ta=a+dir[i][0]; int tb=b+dir[i][1]; if(in(ta,tb)&&gra[ta][tb]=='1') un(code(ta,tb),code(a,b)); } if(b==0)un(code(a,b),S); if(b==m-1)un(code(a,b),T);}int main(){ //freopen("d:\\acm\\in.in","r",stdin); int t; scanf("%d",&t); while(t--) { scanf("%d %d",&n,&m); S=n*m,T=S+1; for(int i=0;i<=T;i++) pre[i]=i; for(int i=0;i<n;i++) scanf("%s",gra[i]); for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(gra[i][j]=='1') add(i,j); int ans=-1; int q; scanf("%d",&q); for(int i=1;i<=q;i++) { int x,y; scanf("%d %d",&x,&y); gra[x][y]='1'; add(x,y); if(ans==-1&&bing(S,T)) ans=i; } printf("%d\n",ans); } return 0;}
- hdu-5652 并查集或者二分BFS
- HDU 5652 二分+并查集+BFS
- HDU 1312 Red and Black(并查集或者BFS)
- 周赛1003题解 hdu 1598 find the most comfortable road (二分+bfs 或者 并查集)
- (并查集 or BFS+二分)HDU5652
- HDU 1598 find the most comfortable road 二分+bfs or 并查集枚举
- hdu 5652 India and China Origins 并查集+BFS
- hdu 5945 bfs+并查集
- ZOJ3811 - Untrusted Patrol(并查集 或者 bfs dfs)
- HDU 3938 离线并查集+二分
- HDU 5652 (二分 bfs)
- hdu 3081 【二分匹配+并查集+删边||最大路+并查集+二分枚举】
- hdu 1181 (搜索BFS,深搜DFS,并查集)
- 【HDU 1198】Farm Irrigation(dfs+并查集+bfs)
- hdu 5876 Sparse Graph(BFS+并查集)
- HDU 3461 Code Lock(并查集+二分求幂)
- hdu 3461 Code Lock(并查集+二分求幂)
- 并查集+二分-hdu-4750-Count The Pairs
- iOS开发总结之项目开发中使用UITableView几百行代码搞定级联表格
- reservoir_sampling
- cordova plugin 安卓原生插件开发笔记(一)
- 1035 Lunch Rush (水题)
- 0欧电阻、电感、磁珠单点接地时的区别
- hdu-5652 并查集或者二分BFS
- 支持向量机SVM(二)
- 个人网站实现扫码登录asp.net 扫码登录
- jquery uploadify在IE上传报406HttpError
- NumPy学习笔记
- HDU 1024 Max Sum Plus Plus【DP,最大m子段和】
- 一天一排序之“希尔排序(缩小增量排序)”
- 支持向量机SVM(三)
- Otto使用记录