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
原创粉丝点击