版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/82713186
题意:
- 有N个学校,每个学校之间单向可以发送软件,现在给你一些学校之间的收发关系。问你下面两个问题:至少要给多少个学校发送软件才能使得最终所有学校都收到软件;至少要多加多少个关系才能使得向任意一个学校发送一套软件,每个学校都能收到软件。
思路:
- 先进行强连通分量拆解,构成新的DAG图
- 在图上找到,入度为0节点的个数,记为
n1
,出度为0节点的个数,记为n2
- 任务1的答案是
n1
,任务2的答案是·max(n1, n2)
- 任务1的答案很好理解,我们简单解释一下任务2正确的原因
- 首先,达到要求的下界,是
max(n1, n2)
,这是因为如果想要任意两点有通路,那么至少每个点都要有出度和入度(必要条件),通过把出度为0的点和入度为0的点直接加边,我们可以在至少加max(n1, n2)
边的情况下,满足我们的必要条件。 - 接下来我们说明可以构造一种方法,使得加
max(n1, n2)
条边就可以,满足我们任务2的要求,那么综合来看max(n1, n2)
就是答案了。 - 先假设,所有点都是位于同一个弱联通分量里的,容易知道必然存在一个入度为0的节点
u
可以到达所有出度为0
的节点。任意找一个出度为0
的节点v
和u
连边(v, u)
。再找一对出度0和入度0的节点对(a, b)
,要求他们直接a
到b
有通路,加边(b, a)
。重复上一步骤至结束。注意每次找的节点对,是尽可能之前没用过的(由于出度0和入度0的节点个数不一样多,尽可能是指保证多的那边不能重复使用)。这样就构建了一个大强连通图了。(具体可以画个图理解) - 在看多个弱联通分量的情况,取两个弱联通分量,加两条边。设两个联通分量里的
u
,v
分别记为u1
,v1
和u2
,v2
,加边(v1, u2)
,(v2, u1)
,就合成了一个弱联通分量。都合并后,再按刚才的思路做,由于(v1, u2)
,(v2, u1)
替代了(v1, u1)
,(v2, u2)
两条边,所以合并并不会引入新的边,故得证。
实现:
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 105;
vector<int> g[maxn];
vector<int> rg[maxn];
bool vis[maxn];
int order[maxn];
bool pre[maxn];
bool suf[maxn];
vector<int> tmp;
int n;
void dfs(int u){
for (int i = 0; i < g[u].size(); i++){
int v = g[u][i];
if (vis[v])
continue;
vis[v] = true;
dfs(v);
}
tmp.push_back(u);
}
void rdfs(int u, int k){
order[u] = k;
for (int i = 0; i < rg[u].size(); i++){
int v = rg[u][i];
if (vis[v])
continue;
vis[v] = true;
rdfs(v, k);
}
}
void add(int u, int v){
g[u].push_back(v);
rg[v].push_back(u);
}
int scc(){
for (int i = 1; i <= n; i++){
if (!vis[i]){
vis[i] = true;
dfs(i);
}
}
memset(vis, 0, n+1);
int k = 0;
for (int i = n - 1; i >= 0; i--){
if (!vis[tmp[i]]){
vis[tmp[i]] = true;
rdfs(tmp[i], k++);
}
}
return k;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for (int i = 1; i <= n; i++){
int v = -1;
while (true){
cin>>v;
if (v == 0)
break;
add(i, v);
}
}
int k = scc();
if (k == 1){
cout << 1 << endl << 0 << endl;
return 0;
}
for (int i = 1; i <= n; i++){
for (int j = 0; j < g[i].size(); j++){
int v = g[i][j];
if (order[i] != order[v]){
pre[order[v]]= true;
suf[order[i]] = true;
}
}
}
int cnt_pre = 0, cnt_suf = 0;
for (int i = 0; i < k; i++){
if (pre[i] == false)
cnt_pre++;
if (suf[i] == false)
cnt_suf++;
}
cout << cnt_pre << endl << max(cnt_suf, cnt_pre)<< endl;
return 0;
}