当前位置: 代码迷 >> 综合 >> 【强连通分量】POJ1236Network of Schools
  详细解决方案

【强连通分量】POJ1236Network of Schools

热度:56   发布时间:2023-09-27 08:54:25.0

题目描述:

【强连通分量】POJ1236Network of Schools


分析

第一问很显然,把强连通分量缩成一个点之后,图中剩下多少个入度为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)条边即可。

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

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 110
using 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]]);}elseif(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));
}