poj1236
- 题意:n个电脑直接有一些有向边(u,v)。下发文件时,如果u得到了且有一条边(u,v),那么v也就得到了。
– 问1:最少下发多少份文件使得所有电脑能接收到文件
– 问2:至少加多少条使得从任意一个地方下发文件,所有人都能够看的到
- 第一问,也就是求有多少个强连通块入度为0。
因为,每个强连通块内都是可以互相到达的,然后..如果这个强连通块入度不为零,那么说明至少会有一个强连通块连向它,那么那个强连通块如果有了文件,次强连通块内的点也就有了文件。
所以,我们只要找到强连通块的入度为零的点的个数就好啦~ - 第二问,也就是求最少添加多少条边成为强连通块。
就如同上一问,只考虑每个强连通块直接的关系就好了。我们可以把没有出度的连向没有入度的,这样就能使得边最少了。然后剩下的就随便连就好了
所以,结果就是 max(入度为零的点的个数m,出度为零的点的个数)
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 110
struct node{int x,y,next;}mp[N*N];
int dfn[N],stack[N],vis[N],low[N],h[N],belong[N],cnt1[N],cnt2[N],tot=0,num=0,n,top=0,scc=0,ans1=0,ans2=0;
void tarjan(int x){
dfn[x]=++tot;low[x]=tot;
vis[x]=1;stack[++top]=x;
for(int i=h[x];i;i=mp[i].next){
int y=mp[i].y;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}else if(vis[y]) low[x]=min(low[x],dfn[y]);
}if(dfn[x]==low[x]){
belong[x]=++scc;vis[x]=0;
while(stack[top]!=x){
belong[stack[top]]=scc;
vis[stack[top--]]=0;
}top--;
}
}
int main(){
freopen("poj1236.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
while(scanf("%d",&x)>0 && x>0){mp[++num].x=i;mp[num].y=x;mp[num].next=h[i];h[i]=num;}
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=num;i++){
int x=belong[mp[i].x],y=belong[mp[i].y];
if(x!=y){cnt1[x]++;cnt2[y]++;}
}
if(scc==1) {printf("1\n0\n");return 0;}//注意!!!
for(int i=1;i<=scc;i++){
if(!cnt1[i]) ans1++;
if(!cnt2[i]) ans2++;
}
printf("%d\n%d\n",ans2,max(ans1,ans2));
return 0;
}