vector表示十字链表+Tarjan+缩点+拓扑排序 详解 poj 2762

题目链接:http://poj.org/problem?id=2762

做法:
1、把强连通分量缩点

2、缩点后的图拓扑排序,这里可知拓扑排序的一个作用:判断是不是任意两个点A,B之间都有办法从A到B

注释比较详细,面贴代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
#define Max 1010

int n,m,low[Max],cnt,scnt,ord[Max],n2,id[Max],map[Max][Max];/* 这里的ord就是dfn并且以-1表示没有被访问过*/
/*low[]记录与i连接的节点最先访问的次序*/
struct Node{
    int e,val;
}item;
/*val可以记录权值,就连通性,似乎此题没这个值也行*/

vector< vector<Node> >g(Max);/*TMD写成vector<vector<Node>>g(Max);没加空格居然通不过编译*/
stack<int>st;

void Tarjan(int e)
{
    int t,i;
    int min=low[e]=ord[e]=cnt++;

    st.push(e);
    for(i=0;i<g[e].size();i++)
    {
        t=g[e][i].e;
        if(ord[t]==-1)Tarjan(t);
        if(low[t]<min)min=low[t];
    }
    if(min<low[e])
    {
        low[e]=min;
        return;
    }
    /*说明有回边,其实这算是Tarjan的一种非主流的写法了,e已经在栈里,
    如min<low[e],就是与e连接的节点在早于e已经被访问过,则必然存在回边*/
    /*下面的是在dfn[i]==low[i]时才去执行的,所以当min<low[e]时low[e]被修改,
    必不再等于dfn[i],故此处就return*/
    do
    {
        id[t=st.top()]=scnt;/*缩点步骤一:id[i]记录第i个点属于第id[i]个连通分量*/
        low[t]=n;/*出栈*/
        st.pop();
    }while(e!=t);
    scnt++;
}

void search(int n)
{
    int i;

    cnt=scnt=0;
    memset(ord,-1,sizeof(ord));
    for(i=0;i<n;i++)
        if(ord[i]==-1)Tarjan(i);
    /*没访问的结点都太监(Tarjan)一遍,防止输入的本身就不是连通图*/
}

void base_vertex()
{
    int i,j,t;

    search(n);
    n2=scnt;
    /*缩点操作二:依据id数组重新做一个map*/
    memset(map,0,sizeof(map));
    for(i=0;i<n;i++)
        for(j=0;j<g[i].size();j++)
            map[id[i]][id[g[i][j].e]]=1;
}
/*拓扑排序*/
int toposort(int n,int *topo)
{
    int d[Max],i,j,k;
    /*计算入度存入d[i]*/
    for(i=0;i<n;i++)
    {
        d[i]=0;
        for(j=0;j<n;j++)d[i]+=map[j][i];
    }
    for(k=0;k<n;k++)
    {
        for(i=0;d[i]&&i<n;i++);/*i就是入度为0的点,靠这个写法挺牛逼的*/
        if(i==n)return 0;
        d[i]=-1;
        for(j=0;j<n;j++)
           d[j]-=map[i][j];
        topo[k]=i;
    }
    return 1;
}

int main()
{
    int ncase,i,j,u,v,topo[Max];

    scanf("%d",&ncase);

    while(ncase--)
    {
        memset(topo,0,sizeof(topo));
        memset(map,0,sizeof(map));
        scanf("%d%d",&n,&m);
        for(i=0;i<=n;i++)
            g[i].clear();
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            item.e=v-1;
            item.val=1;
            g[u-1].push_back(item);
        }
        base_vertex();
        toposort(n2,topo);
        int flag=1;
        for(i=0;i<n2-1;i++)
        {
            if(!map[topo[i]][topo[i+1]])
            {
                flag=0;break;
            }
        }
        if(flag)printf("Yes\n");
        else printf("No\n");
    }

    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值