地址:
点击打开链接
下面,是对一个代码的翻译,现搓的话,有点费劲。
一行一行的翻译:
#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();
}
}