您的位置:首页 > 其它

POJ 1236 Network of Schools(强连通分量缩点)

2015-09-10 21:27 309 查看
题意:n个学校有向图,得到软件的学校可以根据路径发放给其他学校。问1:至少给几个学校发放。问2:给任意一个学校发放,至少添几条边可发放至所有学校。

显然要用Tarjan先找到所有的强连通分量,然后缩点建树。建树之后对每个缩点记其入度出度。显然,对于问题1,只要向入度为0的点发放软件就可满足条件。

那么问题2呢?试想对于所有的子树,每棵子树有根节点和叶节点,要得到强连通分量,显然需要将这些根节点和叶节点连起来。至少连多少呢?答案是max(根节点个数,叶节点个数)。将一棵树的叶节点连到另一棵树的根节点,就可以完成两棵树的连通,消灭掉叶节点和根节点。消灭掉所有的,就得到了一个强连通分量。至少要多少个,就要看根节点和叶节点哪个多了。

代码:

// Header.
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>
using namespace std;

typedef long long LL;
#define mem(a, n) memset(a, n, sizeof(a))
#define rep(i, n) for(int i = 0; i < (n); i ++)
#define REP(i, t, n) for(int i = (t); i < (n); i ++)
#define FOR(i, t, n) for(int i = (t); i <= (n); i ++)
#define ALL(v) v.begin(), v.end()
#define si(a) scanf("%d", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define pb push_back
const int inf = 0x3f3f3f3f, N = 1e2 + 5, MOD = 1e9 + 7;

int T, cas = 0;
int n, m;
struct edge {
int v, next;
}e[N * N];
int dfn
, low
, in
, out
, head
, g
, st
, vis
;
int ne = 0, ans = 0, dfsNum = 0, tot = 0, top = 0, ans1, ans2;
void addEdge(int u, int v) {
e[ne].v = v;
e[ne].next = head[u];
head[u] = ne ++;
}
// Imp
void init() {
mem(dfn, 0);
mem(low, 0);
mem(in, 0);
mem(out, 0);
mem(st, 0);
mem(vis, 0);
mem(head, -1);
dfsNum = ne = top = tot = 0;
}

void Tarjan(int u) {
dfn[u] = low[u] = ++ dfsNum;
st[top++] = u;
vis[u] = 1;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(!dfn[v]) {
Tarjan(v);
low[u] = min(low[u], low[v]);
} else if(vis[v]) low[u] = min(low[u], dfn[v]);
}

if(low[u] == dfn[u]) {
tot ++;
while(1) {
int k = st[--top];
g[k] = tot;
vis[k] = 0;
if(k == u) break;
}
}
}

void work() {
ans1 = ans2 = 0;
FOR(u, 1, n) {
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(g[u] == g[v]) continue;
out[g[u]] ++;
in[g[v]] ++;
}
}
}

void Gao() {
work();
FOR(i, 1, tot) {
if(!in[i]) ans1 ++;
if(!out[i]) ans2 ++;
}

if(tot == 1) printf("1\n0\n");
else printf("%d\n%d\n", ans1, max(ans1, ans2));
}

int main(){
#ifdef LOCAL
freopen("/Users/apple/input.txt", "r", stdin);
//	freopen("/Users/apple/out.txt", "w", stdout);
#endif

si(n);
int v;
init();
FOR(u, 1, n) while(si(v), v) addEdge(u, v);
FOR(i, 1, n) if(!dfn[i]) Tarjan(i);
Gao();

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: