天天写算法之(Tarjan缩点+反向建图+dfs)Hawk-and-Chicken

地址: 点击打开链接
下面,是对一个代码的翻译,现搓的话,有点费劲。

一行一行的翻译:

#include<stdio.h>  
#include<string.h>  
#include<vector>  
#include<iostream>  
#include<algorithm>  
using namespace std;  
#define maxn 5250  
int kase;  
bool map[5250][5250];  //map是用来去掉重边,重边为什么产生??
vector<int >mp[maxn]; // 记录每个结点所连接的结点  
vector<int >mp2[maxn];  //用于反向jian'tu,存储的是一个一个的联通分量
int stack[maxn*1000];  //用于记录访问过的节点
int sum[maxn];  //记录最后节点总数
int degree[maxn]; //反向建图后,巨鹿出度,为了找出度为0的节点 
int vis[maxn];  //dfs用,同时tarjan也用,用来记录访问
int low[maxn];  //记录low,这个不明白的话,就得去看tarjan缩点
int dfn[maxn];  //同上
int color[maxn];  //染色,也就是同样的环内颜色是一致的。
int dp[maxn];  //记录每个团所能连接的最大的点数
int ans[maxn];  //存储结果
int n,m,cnt,tt,sig,leijia;
//主要用于缩点,说染色可能更加形象一些。  
void Tarjan(int u)  
{  
    vis[u]=1;  //将该点设置为已经访问
    low[u]=dfn[u]=cnt++;  //初始化low和dfn  , cnt就是每当一个点是新的,那么就cnt++,累加赋值
    stack[++tt]=u;  //将该点压栈,用数组进行模拟
    //查看该点所连接的所有点
    for(int i=0;i<mp[u].size();i++)  
    {  
        int v=mp[u][i];  //找到该点
        if(vis[v]==0)Tarjan(v);  //如果该点没有被访问过,那么就访问该点
        if(vis[v]==1)low[u]=min(low[u],low[v]);  //如果访问过,那么就更新u点的值,这个地方也是tarjan缩点的规则
    }  
    if(low[u]==dfn[u])  //如果构成了一个连通分量
    {  
        sig++;  //给这个分量分配一个索引
        do  
        {  
            vis[stack[tt]]=-1;  //将该点的访问归位,这个的作用???
            color[stack[tt]]=sig;  
        }  
        while(stack[tt--]!=u);  //将这个分量内所有的点全都赋值为该索引,如果用染色,那么就是把这个连通分量都染成一样的色
    }  
}  
//用于计算每个团,所能收到的最大的节点
void Dfs(int u)  
{  
    vis[u]=1;  //设置访问
    leijia+=sum[u];//对于每个入度为0的点,能够反向建图到达的点统计起来。  
    for(int i=0;i<mp2[u].size();i++)  //看看这个团,联通的所有的团的点。
    {  
        int v=mp2[u][i];  //找到上一个团。
        if(vis[v]==0)Dfs(v);  //如果上一个团没被访问过,访问。
    }  
}  
void Slove()  
{  
    tt=-1;sig=0;cnt=1;  //重置染色
    for(int i=1;i<=n;i++)  
    {  
        if(vis[i]==0)Tarjan(i);  
    }//Tarjan染色  
    for(int i=1;i<=n;i++)  
    {  
        for(int j=0;j<mp[i].size();j++)  
        {  
            int v=mp[i][j];  //找到该点
            if(color[i]!=color[v])  
            {  
                if(map[color[v]][color[i]]==true)continue;  //连通分量已经关联,那么久跳过去,这里针对的是染色集合,为什么???
                mp2[color[v]].push_back(color[i]);  //联通分量之间关联,注意这里是在反向,但是注意这里的整个对象,变成了一个一个的染色集合
                degree[color[i]]++;  //设置出度
                map[color[v]][color[i]]=true;  //连通分量已经关联
            }  
        }  
    }//缩点重新建边,并且去重边。这里注意一下判重数组map【】【】我开的类型是bool的,开int会超内存,很蛋疼  
    memset(vis,0,sizeof(vis));  //这里初始化,为dfs准备
    for(int i=1;i<=n;i++)sum[color[i]]++;//记录每个连通分量的具有多少个点
    for(int i=1;i<=sig;i++)dp[i]=sum[i];//  
    //开始查看各个团内入度为0(这里是反向,所以入度),找个个团的最大方向和
    for(int i=1;i<=sig;i++)  
    {  
        if(degree[i]==0)//对于缩点之后的点,如果入度为0,进入Dfs  ,只有反向过来,入度为0,这个点就是最后的那个中止点
        {  
            memset(vis,0,sizeof(vis));  //
            leijia=0;  //初始化
            Dfs(i);  
            dp[i]=max(dp[i],leijia);  //更新每个团,可以访问的最多的结点数。
        }  
    }  
    int maxnn=0;  
    //找到这些团,里面最大的节点数
    for(int i=1;i<=sig;i++)  
    {  
        maxnn=max(dp[i],maxnn);  
    }//得到这个最大值  
    int ttt=0;  
    for(int i=1;i<=sig;i++)  
    {  
        //寻找哪个团的最大
        if(dp[i]==maxnn)  
        {  
            for(int j=1;j<=n;j++)  
            {  
                //找到哪个节点,被标记和这个团一个颜色。
                if(color[j]==i)  //如果颜色一样
                {  
                    ans[ttt++]=j;//并且将可能的节点都放进答案数组中  
                }  
            }  
        }  
    }  
    sort(ans,ans+ttt);  //按照顺序,输出所有可能的节点
    printf("Case %d: %d\n",++kase,maxnn-1);//去掉自己那个手绢。  
    //把每个点全都输出出来
    for(int i=0;i<ttt;i++)  
    {  
        if(i==0)printf("%d",ans[i]-1);//建图习惯,大家忽视掉就好  
  
        else printf(" %d",ans[i]-1);  
    }  
    printf("\n");  
}  
void init()  
{  
    memset(degree,0,sizeof(degree));  
    memset(map,false,sizeof(map));  
    memset(sum,0,sizeof(sum));  
    memset(dp,0,sizeof(dp));  
    memset(vis,0,sizeof(vis));  
    memset(low,0,sizeof(low));  
    memset(dfn,0,sizeof(dfn));  
    memset(color,0,sizeof(color));  
    memset(dp,0,sizeof(dp));  
}  
int main()  
{  
    int t;  
    kase=0;  
    scanf("%d",&t);  
    while(t--)  
    {  
        scanf("%d%d",&n,&m);  
        init();  
        for(int i=1;i<=n;i++)mp[i].clear(),mp2[i].clear();  
        //把每个点连接着谁都赋值
        for(int i=0;i<m;i++)  
        {  
            int x,y;  
            scanf("%d%d",&x,&y);  
            x++;y++;  
            mp[x].push_back(y);  
        }  
        Slove();  
    }  
}  

猜你喜欢

转载自blog.csdn.net/qq_36616268/article/details/80572119