題目描述:
有一些學校通過單向網絡連通,求出下面兩個問題:
1.至少在多少個學校拷貝一個軟件,使得這個軟件能通過網絡傳遍所有學校。
2.至少加多少條單向網絡,使得任意在一個學校拷貝軟件,就能使得這個軟件通過網絡傳遍所有學校。
大致思路:
如果直接深搜的話複雜度NM肯定超時,所以先用強連通縮點,然後找出入度爲0得點就是第一個問題的答案,而第二個問題就是加多少條邊,使得圖成爲強連通。這個答案就是入度0和出度0點個數中的最大值。
代碼:
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100 + 10;
vector<int> G[maxn];
vector<int> rG[maxn];
int n;
vector<int> vs;
bool used[maxn];
int cmp[maxn];
bool g[maxn][maxn];
void add_edge(int from, int to) {
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v) {
used[v] = true;
for (int i = 0; i < G[v].size(); i++) {
if (!used[G[v][i]]) dfs(G[v][i]);
}
vs.push_back(v);
}
void rdfs(int v, int k) {
used[v] = true;
cmp[v] = k;
for (int i = 0; i < rG[v].size(); i++) {
if (!used[rG[v][i]]) rdfs(rG[v][i], k);
}
}
int scc() {
memset(used,0,sizeof(used));
vs.clear();
for (int i = 1; i <= n; i++) {
if (!used[i]) dfs(i);
}
memset(used,0,sizeof(used));
int k = 0;
for (int i = vs.size() - 1; i >=0; i--) {
if (!used[vs[i]]) rdfs(vs[i], k++);
}
return k;
}
int main() {
while (cin>>n) {
for (int i = 1; i <= n; i++) {
int tmp;
while (cin>>tmp,tmp) {
add_edge(i,tmp);
}
}
int nV = scc();
int v_in[maxn],v_out[maxn];
memset(v_in,0,sizeof(v_in));
memset(v_out,0,sizeof(v_out));
memset(g,0,sizeof(g));
for (int i = 1; i <= n; i++) {
for (int j = 0; j < G[i].size(); j++) {
if (cmp[i] != cmp[G[i][j]]) {
v_in[cmp[G[i][j]]]++;
v_out[cmp[i]]++;
}
}
}
int zi = 0, zo = 0;
for (int i = 0; i < nV; i++) {
if (!v_in[i]) zi++;
if (!v_out[i]) zo++;
}
if (nV == 1) {
cout<<"1"<<endl<<"0"<<endl;
}
else {
cout<<zi<<endl;
cout<<max(zi,zo)<<endl;
}
}
}