【强连通分量】POJ1236Network of Schools

来源:互联网 发布:mac safari总是崩溃 编辑:程序博客网 时间:2024/05/22 09:26

题目描述:

这里写图片描述


分析

第一问很显然,把强连通分量缩成一个点之后,图中剩下多少个入度为0的点,就需要发放多少次软件(这些点没有其他点给他分享),对于入度不为0的点,就一定会从到达它的点向它分享软件。
第二问其实问的就是把一个图,通过连边的方式,缩成一个强连通分量,至少需要多少条边。那么有一个很直观的思路:对于每一个入度为0的点,必须向它连一条边,同样的,对于每一个出度为0的点,必须从它连出去一条边。设有n个入读为0的点,m个出度为0的点,至此,我们能够确定的最小连边数量就是max(n,m)条。
注:这里所说的最小连边数量并不是指只连这么多条边就能满足题意,而是说答案的最小值为max(n,m),换句话说,答案一定不小于这个值。
之后为了讲解方便,我们将入度为0的点成为A类点。
将出度为0的点成为B类点。

接下来,我们需要找一种合理的连图方式,使得连的边数刚好为max(n,m)条。首先,考虑简单一点的情况,假设n=m,很容易想到,
对任意一个B类点i,向一个无法达到i的A类点j相连接,这样一来,凡是能到达i的点,都能够到达j能到达的点。最后,任意一个能到达原B类点的点(其实就是所有点),都能够到达任意一点。
如果不存在无法达到i的A类点,那么说明任意一个点都能到达i,这种情况下,i向任意一个A类点连接都没问题。

现在,再来考虑考虑n≠m的问题
其实很简单,如果A类点多了,就先将多的那些A类点依次连接,最后形成一个n=m的图。
同理,如果B类点多了,也是将多余的B依次连接即可。
转移成n=m的图,需要消耗|n-m|条边,最后再消耗min(n,m)条边即可。

综上,答案就是min(n,m)

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<stack>#include<vector>#define SF scanf#define PF printf#define MAXN 110using namespace std;int dfn[MAXN],low[MAXN],cnt,tot,sum,nx[MAXN];vector<int> a[MAXN];stack<int> s;pair<int,int> l[MAXN*MAXN];int in[MAXN],out[MAXN];void dfs(int x){    dfn[x]=++cnt;    low[x]=dfn[x];    s.push(x);    for(int i=0;i<a[x].size();i++){        if(dfn[a[x][i]]==0){            dfs(a[x][i]);            low[x]=min(low[x],low[a[x][i]]);        }        else            if(nx[a[x][i]]==0)                low[x]=min(low[x],dfn[a[x][i]]);    }    if(low[x]==dfn[x]){        sum++;        while(s.top()!=x){            nx[s.top()]=sum;            s.pop();        }        nx[s.top()]=sum;        s.pop();    }}int n;int main(){    //freopen("data.in","r",stdin);    //freopen("data.out","w",stdout);    SF("%d",&n);    int x,y;    for(int i=1;i<=n;i++){        SF("%d",&x);        while(x){            a[i].push_back(x);            l[++tot]=make_pair(i,x);            SF("%d",&x);        }    }    for(int i=1;i<=n;i++)        if(dfn[i]==0)            dfs(i);    /*PF("(%d)\n",sum);    for(int i=1;i<=n;i++)        PF("[%d:%d]\n",i,nx[i]);*/    for(int i=1;i<=n;i++)        a[i].clear();    for(int i=1;i<=tot;i++){        x=nx[l[i].first];        y=nx[l[i].second];        //PF("(%d %d)\n",x,y);        if(x!=y)            a[x].push_back(y);    }    for(int i=1;i<=sum;i++){        out[i]=a[i].size();        for(int j=0;j<a[i].size();j++)            in[a[i][j]]++;    }    int ans1=0,ans2=0;    if(sum==1){        PF("1\n0");        return 0;    }    for(int i=1;i<=sum;i++){        if(in[i]==0)            ans1++;        if(out[i]==0)            ans2++;    }    PF("%d\n%d",ans1,max(ans1,ans2));}
原创粉丝点击