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