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

/*该题被博客里标记为中等题,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];             //原图tarjan
int 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;
}

posted @ 2014-02-09 20:08  天羽屠龙舞  阅读(338)  评论(0编辑  收藏  举报