题目链接: hdu 2767
题目大意: 给定有向图,问至少加入多少条边可以使得其成为强联通图
解题思路: Kosaraju找联通分量,并缩成点
缩点之后形成一个或者多个DAG,题意不保证图联通
统计缩点后Max(入度为0顶点数,出度为0顶点数)
对于缩点后的图分两种情况:
第一种情况是图中的顶点可以到达:
这个时候只需要将每个入度或出度为0顶点都建立环,也就是添加三条边
第二种情况某些点不能到达:
则将这些点连成一个环,也需要顶点数目条边
代码:
//Final Kosaraju+缩点 至少添加Max(入度为0的顶点数,出度为0的顶点数)的边可成联通图
#pragma comment(linker, "/STACK:10240000000,10240000000")
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 20010
struct snode{
int to,next;
}edge1[MAX*20],edge2[MAX*20];
int n,k,Index1,Index2,pre1[MAX],pre2[MAX],list[MAX],visit[MAX];
int In[MAX],To[MAX],father[MAX];
void Add_edge(int a,int b) //建立正向逆向图
{
if(a==b)
return ;
edge1[Index1].to=b,edge1[Index1].next=pre1[a];
pre1[a]=Index1++;
edge2[Index2].to=a,edge2[Index2].next=pre2[b];
pre2[b]=Index2++;
}
void Add_edge2(int a,int b) //建立缩点之后的图
{
if(a==b)
return ;
edge2[Index2].to=b,edge2[Index2].next=pre2[a];
pre2[a]=Index2++;
In[b]++; To[a]++;
}
void Kosaraju(int u) //Kosaraju搜索联通分量
{
visit[u]=1;
for(int i=pre1[u];i!=-1;i=edge1[i].next)
{
if(!visit[edge1[i].to])
{
Kosaraju(edge1[i].to);
}
}
list[k++]=u; //***
}
void DFS(int u,int Father) //逆向图搜索
{
int i;
visit[u]=2;
father[u]=Father;
for(i=pre2[u];i!=-1;i=edge2[i].next)
{
if(visit[edge2[i].to]==1)
{
DFS(edge2[i].to,Father);
}
}
}
int main()
{
int i,j,m,a,b,c,t,k1,k2;
scanf("%d",&t);
while(t--)
{
Index1=Index2=0;
memset(To,0,sizeof(To));
memset(In,0,sizeof(In));
memset(pre1,-1,sizeof(pre1));
memset(pre2,-1,sizeof(pre2));
memset(visit,0,sizeof(visit));
memset(father,0,sizeof(father));
scanf("%d%d",&n,&m);
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
Add_edge(a,b);
}
for(i=1,k=0;i<=n;i++)
{
if(!visit[i]) //如果那个点从来没有访问过正向访问
{
Kosaraju(i);
}
}
for(j=k-1,c=0;j>=0;j--)
{
if(visit[list[j]]==1) //全部访问过一遍则逆向访问
{
DFS(list[j],++c);
}
}
if(c==1) //如果缩成一个点则已经是联通通图了
{
printf("0\n");
continue;
}
Index2=0;
memset(pre2,-1,sizeof(pre2));
for(i=1;i<=n;i++)
{
for(j=pre1[i];j!=-1;j=edge1[j].next)
{
int v=edge1[j].to;
Add_edge2(father[i],father[v]);
}
}
for(i=1,k1=k2=0;i<=c;i++)
{
if(In[i]==0) //***Max(入度为0的顶点数,出度为0的顶点数);
k1++;
if(To[i]==0) //
k2++;
}
if(k1>k2)
printf("%d\n",k1);
else
printf("%d\n",k2);
}
return 0;
}