poj3694+hdu2460 求桥+缩点+LCA/tarjan
来源:互联网 发布:设计软件图标矢量图 编辑:程序博客网 时间:2024/04/29 11:29
这个题使我更深理解了TARJAN算法,题意:无向图,每添加一条边后文桥的数量,三种解法:(按时间顺序),1,暴力,每每求桥,听说这样能过,我没过,用的hash判重,这次有俩个参数(n->10w,开不了二维的),怎么判?联系2个参数,我想到了用一个函数,像散列一样,定义关系,我随便写了一个hash[x+y+x/y+y/x+x%y+y%x+x|y],一直WA,虽然未过,但是想到了这个,以后2个参数判重可以用之!2.网上学习了算法,将之缩点成树,每个双连通分量用一个点表示,用一个数组tree[i],点i属于tree[i]值,然后记录下桥,重新用一个FATHER【i】来建树,具体见代码。3.学习了算法后,其实不用缩点!直接搞起!因为tarjan算法本生成树!用后面一个点来标记边即可啊!(开始总部知道怎么标记!)并更加理解了树枝边和返祖边,桥是树枝边,无向图的所有边分为树枝边和返祖边(后向边),都是实际存在的。直接在原图上找LCA(按DFN值来判断)即可。
//(原创于2014.2.18)今复习之用,重新理解后,重新编辑方法3。
在hduj交爆栈,前加一句话:即可AC
#pragma comment(linker, "/STACK:10240000000000,10240000000000")// 申请空间
#include<iostream> //暴力,WA#include<vector>#include<cstring>#include<cstdio>using namespace std;vector<vector<int> >edge(100001);int dfn[100001];int low[100001];int visited[100001]; //标记访问int times=0; //时间戳int hash[210001];int num_bridge=0;int min(int a,int b){ if(a<=b)return a; return b;}void tarjan(int u,int fa) //dfs{ dfn[u]=low[u]=++times; int daxiao=edge[u].size(); for(int i=0;i<daxiao;i++) { int child=edge[u][i]; if(visited[child]==0) { visited[child]=1; tarjan(child,u); low[u]=min(low[u],low[child]); if(dfn[u]<low[child]&&hash[u+child+2*u%child+2*child%u+3*u/child+3*child/u]<=1) //是桥 { num_bridge++; } } else if(edge[u][i]!=fa) { low[u]=min(dfn[edge[u][i]],low[u]); } }}int main(){ int n,m; int tcase=1; while (~scanf("%d%d",&n,&m)&&(n||m)) { for(int i=0;i<=n;i++) { edge[i].clear(); } for(int i=0;i<210001;i++) { hash[i]=0; } int a,b; for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); edge[a].push_back(b); edge[b].push_back(a); hash[a+b+2*a%b+2*b%a+3*a/b+3*b/a]++; } int que;scanf("%d",&que); printf("Case %d:\n",tcase); tcase++; while(que--) { times=0; num_bridge=0; for(int i=0;i<=n;i++) { low[i]=dfn[i]=visited[i]=0; } scanf("%d%d",&a,&b); edge[a].push_back(b); edge[b].push_back(a); hash[a+b+2*a%b+2*b%a+3*a/b+3*b/a]++; visited[1]=1; tarjan(1,-1); printf("%d\n",num_bridge); } printf("\n"); } return 0;}
#include<iostream> //方法2,poj 2000MS AC,HOJ RE(stack over)#include<vector>#include<cstring>#include<cstdio>#include<stack>#include<queue>using namespace std;int dfn[100001]; //int low[100001];int visited[100001]; //tarjan标记访问int father[100001]; //缩点后建成一棵树int head[100001]; bool instack[100001]; int tree[100001]; //每个边双连通分量中的点属于一个集合,函数值1-blockint level[100001];bool mark[100001]; //dfs标记bool is_bridge[100001]; //标记桥,形成树后,用点来标记边也可,stack<int>s;vector<vector<int> >bridge(100001);int min(int a,int b){ if(a<=b)return a; return b;}struct edges //边{ int pre,to;};struct bridges //桥{ int from,to;};int times=0; int num_bridge=0; int block; //时间戳,桥数量,“块”数(边双连通分量数)vector<edges>edge(400001); vector<bridges> ve;void tarjan(int u,int fa) //走一遍,求出桥{ dfn[u]=low[u]=++times; instack[u]=1; s.push(u); //入栈, for(int i=head[u];i!=-1;i=edge[i].pre) { int child=edge[i].to; if(visited[child]==0) { visited[child]=1; tarjan(child,u); low[u]=min(low[u],low[child]); if(dfn[u]<low[child]) //是桥,保存起来 { num_bridge++; bridges temp; temp.from=u;temp.to=child; ve.push_back(temp); } } else if(child!=fa) { low[u]=min(dfn[child],low[u]); } } if(dfn[u]==low[u]) //发现一个边双连通分量 blosk++ { block++; int now=s.top(); while(now!=u) { instack[now]=0; s.pop(); tree[now]=block; now=s.top(); } instack[now]=0; s.pop(); tree[now]=block; }}void dfs(int u,int lev) //走一遍DFS,自制缩成一棵树,用father【i】来连接,规定了方向,并记录每个点深度。{ level[u]=lev; int len=bridge[u].size(); for(int i=0;i<len;i++) { int v=bridge[u][i]; if(mark[v]==0) { father[v]=u; mark[v]=1; dfs(v,lev+1); } }}void lca(int u,int v) //每次询问添加的边,调用一次LCA,将路经上(按father和深度level向上走)的标记为非桥。{ if(level[u]>level[v]){int temp=v;v=u;u=temp;} while(level[v]>level[u]) { if(is_bridge[v]) { num_bridge--; is_bridge[v]=0; } v=father[v]; } while(v!=u) { if(is_bridge[v]) { num_bridge--; is_bridge[v]=0; } if(is_bridge[u]) { num_bridge--; is_bridge[u]=0; } v=father[v]; u=father[u]; }}int main(){ int n,m; int tcase=1; while (~scanf("%d%d",&n,&m)&&(n||m)) { for(int i=0;i<100001;i++) { level[i]=mark[i]=tree[i]=father[i]=low[i]=dfn[i]=visited[i]=0; head[i]=-1; bridge[i].clear(); is_bridge[i]=1; } ve.clear(); int a,b; num_bridge=block=times=0; for(int i=0;i<2*m;i++) //读入 { scanf("%d%d",&a,&b); edge[i].to=b; edge[i].pre=head[a]; head[a]=i; i++; edge[i].to=a; edge[i].pre=head[b]; head[b]=i; } visited[1]=1; tarjan(1,-1); for(int i=0;i<ve.size();i++) { bridge[tree[ve[i].from]].push_back(tree[ve[i].to]); bridge[tree[ve[i].to]].push_back(tree[ve[i].from]); } mark[1]=1; dfs(1,0); int que;scanf("%d",&que); printf("Case %d:\n",tcase); tcase++; while(que--) { scanf("%d%d",&a,&b); lca(tree[a],tree[b]); printf ("%d\n",num_bridge); } printf("\n"); } return 0;}
方法3:
#pragma comment(linker, "/STACK:10240000000000,10240000000000")// 申请空间,否则爆。Hdu 460MS ac#include<iostream> //poj 1000MS AC,#include<vector>#include<cstring>#include<cstdio>using namespace std;int dfn[100001];int low[100001];bool visited[100001]; //tarjan标记访问int father[100001]; //缩点后建成一棵树int head[100001]; //每个边双连通分量中的点属于一个集合,函数值1-blockbool is_bridge[100001]; //标记桥,形成树后,用点来标记边也可,int min(int a,int b){ if(a<=b)return a; return b;}struct edges //前向星保存边{ int pre,to;};int times=0; int num_bridge=0; //时间戳,桥数量,vector<edges>edge(400001); void tarjan(int u,int fa) //走一遍,求出桥,不忘了。dfs生成的是树!所以可以标记后面一个点来标记边!无向图tarjan俩个变量{ dfn[u]=low[u]=++times; for(int i=head[u];i!=-1;i=edge[i].pre) { int child=edge[i].to; if(visited[child]==0) //理解这里是树枝边(向前)! { visited[child]=1; father[child]=u; //生成树的父亲 tarjan(child,u); low[u]=min(low[u],low[child]); if(dfn[u]<low[child]) //是桥,保存起来,标记后一个点即可(生成树每个点对应一条到它的边) { num_bridge++; is_bridge[child]=1; } } else if(child!=fa) //返祖边(向后边)点已经访问,说明child是u的某个祖先! { low[u]=min(dfn[child],low[u]); } }}void lca(int u,int v) //每次询问添加的边,调用一次LCA,将路经上(按father和dfn向上走)的标记为非桥。dfn恰好是树的的层次。{ if(dfn[u]>dfn[v]){int temp=v;v=u;u=temp;} while(dfn[v]>dfn[u]) //到同一层为止。 { if(is_bridge[v]) { num_bridge--; is_bridge[v]=0; } v=father[v]; } while(v!=u) //到公共祖先为止。 { if(is_bridge[v]) { num_bridge--; is_bridge[v]=0; } if(is_bridge[u]) { num_bridge--; is_bridge[u]=0; } v=father[v]; u=father[u]; }}int main(){ int n,m; int tcase=1; while (~scanf("%d%d",&n,&m)&&(n||m)) { for(int i=0;i<100001;i++) { dfn[i]=father[i]=low[i]=dfn[i]=visited[i]=0; head[i]=-1; is_bridge[i]=0; } int a,b; num_bridge=times=0; for(int i=0;i<2*m;i++) //读入 { scanf("%d%d",&a,&b); edge[i].to=b; edge[i].pre=head[a]; head[a]=i; i++; edge[i].to=a; edge[i].pre=head[b]; head[b]=i; } visited[1]=1; tarjan(1,-1); int que;scanf("%d",&que); printf("Case %d:\n",tcase); tcase++; while(que--) { scanf("%d%d",&a,&b); lca(a,b); printf ("%d\n",num_bridge); } printf("\n"); } return 0;}
2 0
- poj3694+hdu2460 求桥+缩点+LCA/tarjan
- hdu2460 poj3694 求桥 + 求lca
- hdu2460&&poj3694 缩点+lca变形
- POJ3694-Network(Tarjan缩点+LCA)
- poj3694 Network 无向图tarjan求桥+LCA
- POJ3694(tarjan缩点+并查集+LCA)
- poj3694 Network 【图论-Tarjan-Lca】
- hdu2460 和 poj3694
- LCA+桥poj3694
- poj3694 LCA+并查集+tarjan求割边
- poj3694 Network 边双联通缩点+离线LCA
- poj3694--Network(双连通缩点+lca)
- tarjan缩点/求桥模板
- HDU 2460 Network 求桥(tarjan)+LCA
- Network(Tarjan+缩点+LCA)
- tarjan求lca
- tarjan离线求lca
- tarjan求LCA
- 一个win32应用程序文件的启动过程
- 队列的特殊实现方式
- 六度分离 hdu Floyd算法基础
- Android中Preference的使用以及监听事件分析
- linux下eclipse向args传递参数
- poj3694+hdu2460 求桥+缩点+LCA/tarjan
- android开发 drawable(hdpi,mdpi,ldpi)的区别
- std::string,结果同预期不一样
- 『黑马程序员』---java--基础加强--泛型
- C++ find和find_if
- 任意长度的两个正整数相乘
- VC6.0 error LNK2001: unresolved external symbol _main解决办法
- CentOS 6.4 添加永久静态路由所有方法汇总
- Eclipse集成Tomcat