网络协议

https://loj.ac/problem/10093

题目描述

  给出一些学校之间援助关系(单向),有两个任务:A求最少给多少个学校就能使所有学校收到援助;B求最少添加多少条援助关系使得把援助发给任意一个学校所有学校都能收到援助。

思路

  tarjan的模板题。对于每一个强连通分量,内部节点之间可以互相援助,可以直接缩点。对缩点之后的DAG,对于任务A,显然我们只需要考虑把软件发给入度为0的节点那么其他节点必定受到软件;而任务B,我们就是要添最少边使这张图变为强连通分量。对于强连通分量中必定由一些环组成,所以我们把入度为0和出度为0的点连边,答案即为入度为0的点和出度为0的节点数中的较大值。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=110,M=1e4+10;

struct Edge
{
    int x,y;
}e[M];

int nxt[M],to[M],head[N],tot;
void add_edge(int x,int y)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    e[tot].x=x;e[tot].y=y;
}

int read()
{
    int res=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
    return res*w;
}

int dfn[N],co[N],col,st[N],top,low[N],idx;
void tarjan(int u)
{
    dfn[u]=low[u]=++idx;
    st[++top]=u;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!co[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        co[u]=++col;
        while(st[top]!=u)
        {
            co[st[top]]=col;
            --top;
        }
        --top;
    }
}

int out[N],in[N];
int main() 
{
    int n;
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        while(x!=0)
        {
            add_edge(i,x);
            x=read();
        }
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);
    for(int i=1;i<=tot;i++)
    {
        int u=co[e[i].x],v=co[e[i].y];
        if(u!=v)
        {
            out[u]++;
            in[v]++;
        }
    }
    int cnt1=0,cnt2=0;
    for(int i=1;i<=col;i++)
    {
        if(!in[i])cnt1++;
        if(!out[i])cnt2++;
    }
    if(col!=1)printf("%d\n%d",cnt1,max(cnt1,cnt2));
    else printf("%d\n0",cnt1);
    return 0;
}

 

posted @ 2019-10-23 19:54  fbz  阅读(121)  评论(0编辑  收藏  举报