Tarjan系列算法总结(hdu 1827,4612,4587,4005)

  tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下。我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识。tarjan嘛,就是维护一个dfsnum[]和一个low[],在dfs树上处理图的连通性等问题。细节处的不同导致算法本身的不同作用。

 

  有向图强连通缩点:大体思路是维护一个栈,满足访问一个点就push到栈里面,当满足low[now]==dfn[now]时出栈,用dfn[]更新low[]当且仅当下一个点在栈内(注意不是递归栈)。  

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 2200
#define MAXV MAXN
#define MAXE MAXV*2
#define INF 0x3f3f3f3f
struct Edge
{
        int np;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y)
{
        E[++tope].np=y;
        E[tope].next=V[x];
        V[x]=&E[tope];
}
int status[MAXN];
int stack[MAXN],tops=-1;
int low[MAXN],dfn[MAXN],dfstime;
int top[MAXN];
void tarjan(int now)
{
        status[now]=1;
        stack[++tops]=now;
        low[now]=dfn[now]=++dfstime;
        Edge *ne;
        for (ne=V[now];ne;ne=ne->next)
        {
                if (status[ne->np]==1)
                {
                        low[now]=min(low[now],dfn[ne->np]);
                }else if (status[ne->np]==0)
                {
                        tarjan(ne->np);
                        low[now]=min(low[now],low[ne->np]);
                }
        }
        if (low[now]==dfn[now])
        {
                while (stack[tops]!=now)
                {
                        status[stack[tops]]=2;
                        top[stack[tops--]]=now;
                }
                status[stack[tops]]=2;
                top[stack[tops--]]=now;
        }
        //status[now]=2;
}
int a[MAXN];
int el[MAXE][2];
int degi[MAXN];
int cirv[MAXN];

int main()
{
        freopen("input.txt","r",stdin);
        int n,m,x,y,z;
        while (~scanf("%d%d",&n,&m))
        {
                for (int i=1;i<=n;i++)
                        scanf("%d",a+i);
                for (int i=1;i<=m;i++)
                {
                        scanf("%d%d",&x,&y);
                        addedge(x,y);
                        el[i][0]=x;
                        el[i][1]=y;
                }
                for (int i=1;i<=n;i++)
                        if (!dfn[i])
                                tarjan(i);
                for (int i=1;i<=m;i++)
                {
                        if (top[el[i][0]]!=top[el[i][1]])
                                degi[top[el[i][1]]]++;
                }
                memset(cirv,INF,sizeof(cirv[0])*(n+10));
                for (int i=1;i<=n;i++)
                        cirv[top[i]]=min(cirv[top[i]],a[i]);
                int ans2=0,ans1=0;
                for (int i=1;i<=n;i++)
                {
                        if (top[i]==i && !degi[top[i]])
                        {
                                ans1++;
                                ans2+=cirv[top[i]];
                        }
                }
                printf("%d %d\n",ans1,ans2);
                memset(V,0,sizeof(V[0])*(n+10));
                memset(dfn,0,sizeof(dfn[0])*(n+10));
                memset(degi,0,sizeof(degi[0])*(n+10));
                memset(status,0,sizeof(status[0])*(n+10));
                tope=-1;
                dfstime=0;
        }
}
hdu 1827

  

  无向图求桥边:这种类型算是最简单的把,唯一要注意的是要记录每个“编号”的边是否访问,而不是点。

  这道题我认认真真写的非递归tarjan,参考集训某些大神的非递归,让我领会到了非递归的“精髓”,以后再也不用类似于记录当前运行到什么语句的方法了。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 210000
#define MAXV MAXN
#define MAXE 1100000*2
#define INF 0x3f3f3f3f
struct Edge
{
        int np;
        int id;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y,int id=0)
{
        E[++tope].np=y;
        E[tope].next=V[x];
        E[tope].id=id;
        V[x]=&E[tope];
}
int status[MAXN];
int low[MAXN],dfn[MAXN],dfstime;
bool is_bridge[MAXE];
bool edge_vis[MAXE];
void tarjan(int now)
{
        Edge *ne;
        status[now]=1;
        low[now]=dfn[now]=++dfstime;
        for (ne=V[now];ne;ne=ne->next)
        {
                if (edge_vis[ne->id])continue;
                if (status[ne->np]==1)
                {
                        low[now]=min(low[now],dfn[ne->np]);
                }else if (status[ne->np]==0)
                {
                        edge_vis[ne->id]=true;
                        tarjan(ne->np);
                        low[now]=min(low[now],low[ne->np]);
                        if (low[ne->np]==dfn[ne->np])
                                is_bridge[ne->id]=true;
                }
        }
}
int tarj_now[MAXN],tarj_step[MAXN];
Edge *tarj_ne[MAXN];
int lev=-1;
#define ne tarj_ne[cur]
#define now tarj_now[cur]
void tarjan2(int root)
{
        int cur=0;
        now=root;
        ne=V[root];
        status[now]=1;
        low[now]=dfn[now]=++dfstime;
        while (true)
        {
tarj_begin:
                if (ne)
                {
                        if (!edge_vis[ne->id])
                        {
                                if (status[ne->np]==1)
                                        low[now]=min(low[now],dfn[ne->np]);
                                else if (status[ne->np]==0)
                                {
                                        edge_vis[ne->id]=true;
                                        tarj_now[cur+1]=ne->np;
                                        tarj_ne[cur+1]=V[tarj_now[cur+1]];
                                        cur++;
                                        status[now]=true;
                                        low[now]=dfn[now]=++dfstime;
                                        goto tarj_begin;
tarj_back:
                                        low[now]=min(low[now],low[ne->np]);
                                        if (low[ne->np]==dfn[ne->np])
                                                is_bridge[ne->id]=true;
                                }
                        }
                        ne=ne->next;
                }else
                {
                        cur--;
                        if (cur==-1)break;
                        goto tarj_back;
                }
        }
}
#undef now
#undef ne
int el[MAXE][2];
int uf[MAXN];
int rk[MAXN];
int get_fa(int now)
{
        return now==uf[now] ? now : uf[now]=get_fa(uf[now]) ;
}
void comb(int x,int y)
{
        x=get_fa(x);
        y=get_fa(y);
        if (rk[x]>rk[y])
        {
                uf[y]=x;
                rk[x]=max(rk[x],rk[y]+1);
        }else
        {
                uf[x]=y;
                rk[y]=max(rk[y],rk[x]+1);
        }
}
int dis[MAXN];
int q[MAXN];
int pnt[MAXN];
int bfs(int now)
{
        int head=-1,tail=0;
        Edge *ne;
        q[0]=now;
        dis[now]=0;
        pnt[now]=now;
        int res=now;
        while (head<tail)
        {
                now=q[++head];
                if (dis[res]<dis[now])res=now;
                for (ne=V[now];ne;ne=ne->next)
                {
                        if (ne->np==pnt[now])continue;
                        q[++tail]=ne->np;
                        dis[ne->np]=dis[now]+1;
                        pnt[ne->np]=now;
                }
        }
        return res;
}

int main()
{
        freopen("input.txt","r",stdin);
        int n,m,x,y,z;
        while (scanf("%d%d",&n,&m),n+m)
        {
                memset(V,0,sizeof(V));
                tope=-1;
                dfstime=0;
                memset(status,0,sizeof(status));
                memset(is_bridge,0,sizeof(is_bridge));
                memset(edge_vis,0,sizeof(edge_vis));
                memset(dfn,0,sizeof(dfn));
                int tot=0;
                for (int i=1;i<=n;i++)
                        uf[i]=i,rk[i]=1;
                for (int i=0;i<m;i++)
                {
                        scanf("%d%d",&x,&y);
                        addedge(x,y,i);
                        addedge(y,x,i);
                        el[i][0]=x;
                        el[i][1]=y;
                }
                for (int i=1;i<=n;i++)
                        if (!dfn[i])
                                tarjan2(i);
                for (int i=0;i<m;i++)
                {
                        if (is_bridge[i])
                                tot++;
                        else
                                comb(el[i][0],el[i][1]);
                }
                for (int i=1;i<=n;i++)
                        uf[i]=get_fa(i);
                memset(V,0,sizeof(V));
                tope=-1;
                for (int i=0;i<m;i++)
                {
                        if (get_fa(el[i][0])==get_fa(el[i][1]))continue;
                        addedge(get_fa(el[i][0]),get_fa(el[i][1]));
                        addedge(get_fa(el[i][1]),get_fa(el[i][0]));
                }
                x=1;
                x=bfs(get_fa(x));
                x=bfs(x);
                printf("%d\n",tot-dis[x]);
        }
}
hdu 4612

 

  无向图求割点:这种类型很容易想错,而且也很难调试,首先我们需要对dfs的根节点分类讨论,其次,如果一个点有不止一次满足向下调用low[to]>=dfn[now]或不存在low[to]<=low[pnt],则这个点是割点。

  顺便吐槽一下,网上粘的标程没有一个靠谱的,真不知道数据要水成啥样才能把它们放过。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 5500
#define MAXE MAXN*2
#define INF 0x3f3f3f3f
#define MAXV MAXN
struct Edge
{
        int np,id;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y,int id)
{
        E[++tope].np=y;
        E[tope].id=id;
        E[tope].next=V[x];
        V[x]=&E[tope];
}
bool edge_vis[MAXE];
int low[MAXN],dfn[MAXN];
int dfstime=0;
int root;
int ans;
bool cut_p[MAXN];
int status[MAXN];
void tarjan(int now,int p)
{
        int totd=0;
        dfn[now]=low[now]=++dfstime;
        Edge *ne;
        for (ne=V[now];ne;ne=ne->next)
        {
                if (ne->np==p)continue;
                if (!dfn[ne->np])
                {

                        tarjan(ne->np,now);
                        if (low[ne->np]>=dfn[now])
                                totd++;
                        low[now]=min(low[now],low[ne->np]);
                }else
                {
                        low[now]=min(low[now],dfn[ne->np]);
                }
        }
        ans=max(ans,(totd+(now!=root))-1);
}
int el[MAXE][2];


int main()
{
        freopen("input.txt","r",stdin);
        int x,y,z,n,m;
        while (~scanf("%d %d\n",&n,&m))
        {
                for (int i=0;i<m;i++)
                {
                        scanf("%d%d",&x,&y);
                        x++;y++;
                        el[i][0]=x;
                        el[i][1]=y;
                }
                int res=0;
                int tot;
                for (int i=1;i<=n;i++)
                {
                        ans=-INF;
                        tot=0;
                        memset(V,0,sizeof(V));
                        tope=-1;
                        dfstime=0;
                        memset(dfn,0,sizeof(dfn));
                        for (int j=0;j<m;j++)
                                if (el[j][0]!=i && el[j][1]!=i)
                                        addedge(el[j][0],el[j][1],j),
                                                addedge(el[j][1],el[j][0],j);
                        for (int j=1;j<=n;j++)
                        {
                                if (j==i)continue;
                                if (!dfn[j])
                                {
                                        tot++;
                                        root=j;
                                        tarjan(j,j);
                                }
                        }
                        res=max(res,tot+ans);
                }
                printf("%d\n",res);
        }
}
hdu 4587

   

  点双连通分量:同求桥边。网上都说这是边双,根本搞不清楚。这道题主要是细节坑人,其他都没什么。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 10100
#define MAXV MAXN
#define MAXE MAXN*20
#define INF 0x3f3f3f3f
struct Edge
{
        int np,id,val;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y,int z,int id)
{
        E[++tope].np=y;
        E[tope].val=z;
        E[tope].id=id;
        E[tope].next=V[x];
        V[x]=&E[tope];
}
int dfn[MAXN],low[MAXN],top[MAXN],dfstime;
bool edge_vis[MAXE];
int stack[MAXN],tops=-1;
vector<int> bridge;
void tarjan(int now)
{
        Edge *ne;
        low[now]=dfn[now]=++dfstime;
        stack[++tops]=now;
        for (ne=V[now];ne;ne=ne->next)
        {
                if (edge_vis[ne->id])continue;
                edge_vis[ne->id]=true;
                if (dfn[ne->np])
                {
                        low[now]=min(low[now],dfn[ne->np]);
                }else
                {
                        tarjan(ne->np);
                        low[now]=min(low[now],low[ne->np]);
                        if (low[ne->np]==dfn[ne->np])
                        {
                                while (stack[tops]!=ne->np)
                                        top[stack[tops--]]=ne->np;
                                top[stack[tops--]]=ne->np;
                                bridge.push_back(ne->id);
                        }
                }
        }
}

struct edge
{
        int x,y,z,id;
}el[MAXE],eb[MAXE];
bool cmp_z(edge e1,edge e2)
{
        return e1.z<e2.z;
}
int q[MAXN];
int pnt[MAXN];
int depth[MAXN];
void bfs(int now)
{
        int head=-1,tail=0;
        Edge *ne;
        q[0]=now;
        pnt[now]=now;
        depth[now]=1;
        while (head<tail)
        {
                now=q[++head];
                for (ne=V[now];ne;ne=ne->next)
                {
                        if (ne->np==pnt[now])continue;
                        q[++tail]=ne->np;
                        depth[ne->np]=depth[now]+1;
                        pnt[ne->np]=now;
                }
        }
        return ;
}
int jump[20][MAXN];
void init_lca(int n)
{
        for (int i=1;i<=n;i++)
                jump[0][i]=pnt[i];
        for (int j=1;j<20;j++)
                for (int i=1;i<=n;i++)
                        jump[j][i]=jump[j-1][jump[j-1][i]];
}
int lca(int x,int y)
{
        if (depth[x]<depth[y])
                swap(x,y);
        int dep=depth[x]-depth[y];
        for (int i=0;i<20;i++)
                if (dep&(1<<i))
                        x=jump[i][x];
        if (x==y)return x;
        for (int i=19;i>=0;i--)
                if (jump[i][x]!=jump[i][y])
                        x=jump[i][x],y=jump[i][y];
        return pnt[x];
}
int main()
{
        freopen("input.txt","r",stdin);
        int n,m;
        int x,y,z;
        while (~scanf("%d%d",&n,&m))
        {
                memset(V,0,sizeof(V));
                memset(dfn,0,sizeof(dfn));
                tope=-1;
                bridge.clear();
                memset(pnt,0,sizeof(pnt));
                memset(edge_vis,0,sizeof(edge_vis));
                dfstime=0;
                for (int i=0;i<m;i++)
                {
                        scanf("%d%d%d",&x,&y,&z);
                        el[i].x=x;
                        el[i].y=y;
                        el[i].z=z;
                        el[i].id=i;
                        addedge(x,y,z,i);
                        addedge(y,x,z,i);
                }
                for (int i=1;i<=n;i++)
                {
                        if (!dfn[i])
                        {
                                tarjan(i);
                                while (~tops)
                                        top[stack[tops--]]=i;
                        }
                }
                memset(V,0,sizeof(V));
                tope=-1;
                int l=bridge.size();
                for (int i=0;i<bridge.size();i++)
                {
                        eb[i]=el[bridge[i]];
                        eb[i].x=top[eb[i].x];
                        eb[i].y=top[eb[i].y];
                        addedge(eb[i].x,eb[i].y,0,0);
                        addedge(eb[i].y,eb[i].x,0,0);
                }
                bfs(top[1]);
                init_lca(n);
                sort(eb,eb+l,cmp_z);
                int l2,l1,hh;
                int a;
                l1=eb[0].x,hh=eb[0].y;
                l2=-1;
                if (depth[l1]<depth[hh])swap(l1,hh);
                int res=-1;
                for (int i=1;i<l;i++)
                {
                        for (int j=0;j<2;j++)
                        {
                                a=j?eb[i].y:eb[i].x;
                                if (lca(a,l1)==l1)
                                {
                                        l1=a;
                                }else if (~l2 && lca(a,l2)==l2)
                                {
                                        l2=a;
                                }else if (~l2 && lca(a,l2)==a && lca(a,hh)==hh)
                                {
                                        //okay
                                }else if (lca(a,l1)==a && lca(a,hh)==hh)
                                {
                                        //okay
                                }else if (lca(a,hh)==a && l2==-1)
                                {
                                        hh=a;
                                }else if (depth[lca(a,l1)]<=depth[hh] && l2==-1)
                                {
                                        l2=a;
                                        hh=lca(a,l1);
                                }else
                                {
                                        res=eb[i].z;
                                        break;
                                }
                        }
                        if (~res)break;
                }
                printf("%d\n",res);
        }
}
hdu 4005

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。