poj2767,单向连通图判定,缩点+重新建图+新图DFS

来源:互联网 发布:计算机专业除了编程 编辑:程序博客网 时间:2024/05/22 13:08
/*该题被博客里标记为中等题,30分钟内1A,掌握了算法就简单了,单向连通图判定,单向连通图缩点后必然唯一存在出度为0的点和入度为0的点,并且从入度为0的点出发,可以遍历所有点后到达出度为0点(一条长链),(容易反证),所以缩点后,我重新建图(以前觉得重新建图好麻烦,现在看来SO easy),,对新有向无环图,从入度为0的点做dfs(回溯时要标记回来,因为要所有dfs路线),有一条深度到达强连通分支数即为yes,反之no*/#include<iostream>  //297MS, 1A,简单题。#include<vector>#include<cstdio>#include<cstring>#include<stack>using namespace std;int n;int m;const int MAX=1001;vector<vector<int> >edges(MAX);  //原图vector<vector<int> >newedge(MAX);//缩点后图int visited[MAX];             //原图tarjanint low[MAX];int dfn[MAX];bool mark[MAX];                //新图dfs标记int Strongly_connected_branch[MAX];   //并为一个强连通,标记为1.2.3...int num;int times;      stack<int>s;            bool instack[MAX];int ind[MAX];         //入度数组void tarjan(int u){    low[u]=dfn[u]=times++;    instack[u]=1;    s.push(u);    int len=edges[u].size();    for(int i=0;i<len;i++)    {        int v=edges[u][i];        if(visited[v]==0)        {             visited[v]=1;               tarjan(v);            if(low[u]>low[v])low[u]=low[v];        }        else if(instack[v]&&low[u]>dfn[v])   //有向图,要问是否在栈中,后向边,V为U某个祖先        {            low[u]=dfn[v];        }    }    if(dfn[u]==low[u])          //在一个SCC    {        num++;int temp;         do        {             temp=s.top();             instack[temp]=0;            s.pop();            Strongly_connected_branch[temp]=num;        } while(temp!=u);    }}bool is_no; void dfs(int u,int depth)      //新图的dfs{    if(depth==num)is_no=true;    int len=newedge[u].size();    for(int i=0;i<len;i++)    {        int v=newedge[u][i];        if(mark[v]==0)          {              mark[v]=1;              dfs(v,depth+1);              mark[v]=0;          }    }}void readin(){    scanf("%d%d",&n,&m);    int from,to;    for(int i=1;i<=m;i++)    {        scanf("%d%d",&from,&to);        edges[from].push_back(to);    }}void initialize()       //初始化{    is_no=num=times=0;    for(int i=0;i<=n;i++)    {        ind[i]=0;        mark[i]=instack[i]=low[i]=dfn[i]=visited[i]=0;        edges[i].clear();        newedge[i].clear();        Strongly_connected_branch[i]=-1;    }}void solve(){    for(int i=1;i<=n;i++)       if(visited[i]==0)        {            visited[i]=1;            tarjan(i);        }     for(int i=1;i<=n;i++)         //重新建图,不在同一个SCB的边留下即可。    {        int len=edges[i].size();       for(int j=0;j<len;j++)       {          int v=edges[i][j];          if(Strongly_connected_branch[v]!=Strongly_connected_branch[i])  //注意用SCB序号作代表元(新节点)来重新建图          {             ind[Strongly_connected_branch[v]]++;             newedge[Strongly_connected_branch[i]].push_back(Strongly_connected_branch[v]);          }       }    }    int is_0ind;    for(int i=1;i<=num;i++)    //找到入度为0的点,做起点dfs    {       if(ind[i]==0)is_0ind=i;    }    mark[is_0ind]=1;    dfs(is_0ind,1);    if(is_no)printf("Yes\n");    else printf("No\n");}int main(){    int tcase;    scanf("%d",&tcase);   while(tcase--)   {       initialize();       readin();       solve();   }   return 0;}

0 0