题目
一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意如果 B 在 A 学校的分发列表中,那么 A 不一定也在 B 学校的列表中。
你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。
题解
对于任务A,入度为0的强连通分量个数即为答案
对于任务B,答案取入度为0和出度为0的强连通分量最大值,当只有一个强连通分量时答案为0
可以用tarjan求强连通分量,然后计算入度为0和出度为0的强连通分量个数
我改了大半天,才发现tarjan打错了!!
while (t>0)
{
int j=y[t];
if (dfn[j]==0)
{
tarjan(j);
if (low[j]< low[i])
low[i]=low[j];
} else
if (b[j]&&dfn[j]< low[i])//只有在栈中的点才能更新low[i]!!!!!!!!!
low[i]=dfn[j];
t=ne[t];
}
代码
tarjan
#include <cstdio>
#include <cstring>
using namespace std;
const int ma=10004;
int ls[101],ne[ma],y[ma],dfn[101],low[101],sta[101],b[101];
int lls[101],nne[ma],yy[ma];
int de,tot,c0,r0,sum,n;
int max(int a,int b)
{
if (a>b) return(a);
return(b);
}
void tarjan(int i)
{
de++;
dfn[i]=de;
low[i]=de;
tot++;
sta[tot]=i;
b[i]=1;
int t=ls[i];
while (t>0)
{
int j=y[t];
if (dfn[j]==0)
{
tarjan(j);
if (low[j]<low[i])
low[i]=low[j];
} else
if (b[j]&&dfn[j]<low[i])
low[i]=dfn[j];
t=ne[t];
}
int d[101],e[101],su=0;
memset(d,0,sizeof(d));
memset(e,0,sizeof(e));
if (dfn[i]==low[i])
{
int j=sta[tot];
while (true)
{
j=sta[tot];
tot--;
b[j]=0;
d[j]=1;
e[++su]=j;
if (j==i) break;
}
sum++;
int re=1,ce=1;
for (int j=1;j<=su;j++)
{
t=lls[e[j]];
while (t>0&&re==1)
{
if (!d[yy[t]]) re=0;
t=nne[t];
}
t=ls[e[j]];
while (t>0&&ce==1)
{
if (!d[y[t]]) ce=0;
t=ne[t];
}
}
if (re==1) r0++;
if (ce==1) c0++;
}
}
int main()
{
int su=0;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int t,e;
scanf("%d",&t);
while (t!=0)
{
su++;
ne[su]=ls[i];ls[i]=su;y[su]=t;
nne[su]=lls[t];lls[t]=su;yy[su]=i;
scanf("%d",&t);
}
}
memset(b,0,sizeof(b));
for (int i=1;i<=n;i++)
if (dfn[i]==0)
tarjan(i);
printf("%d\n",r0);
if (sum==1) printf("0"); else printf("%d",max(r0,c0));
}
Kosaraju
Kosaraju就是先正着深搜,记录退出顺序。然后按倒着的退出顺序在深搜,此时所有可以到达的点(不在栈中)都属于同一强连通分量。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int w=101,z=10001;
int ls[w],y[z],ne[z],lls[w],yy[z],nne[z],d[w],e[w],f[w],h[w];
int n,c,g,c0,r0,sum;
void dfs(int i)
{
d[i]=1;
int t=ls[i];
while (t>0)
{
int j=y[t];
if (d[j]==0) dfs(j);
t=ne[t];
}
e[++c]=i;
}
void search(int i)
{
d[i]=1;
g++;
h[i]=1;
f[g]=i;
int t=lls[i];
while (t>0)
{
int j=yy[t];
if (d[j]==0) search(j);
t=nne[t];
}
}
int main()
{
int su=0;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
while (t>0)
{
su++;
ne[su]=ls[i];ls[i]=su;y[su]=t;
nne[su]=lls[t];lls[t]=su;yy[su]=i;
scanf("%d",&t);
}
}
memset(d,0,sizeof(d));
for (int i=1;i<=n;i++)
if (d[i]==0)
dfs(i);
c=0;
memset(d,0,sizeof(d));
for (int i=n;i>=1;i--)
if (d[e[i]]==0)
{
g=0;
memset(h,0,sizeof(h));
search(e[i]);
int ce=0,re=0;
for (int j=1;j<=g;j++)
{
int t=ls[f[j]];
while (t>0&&ce==0)
{
int x=y[t];
if (!h[x]) ce=1;
t=ne[t];
}
t=lls[f[j]];
while (t>0&&re==0)
{
int x=yy[t];
if (!h[x]) re=1;
t=nne[t];
}
}
if (ce==0) c0++;
if (re==0) r0++;
sum++;
}
printf("%d\n",r0);
if (sum==1) printf("0\n"); else printf("%d\n",max(r0,c0));
}