原题地址:http://codeforces.com/contest/999/problem/E
题意:给出一个有向图,然后给出一个中心点s,询问你添加最少几条边以后,使得中心点s可以到达其他任何一个点。
思路:这题有两种做法。
做法一:使用tarjan缩点来做。求出这个有向图的所有强连通分量之后重新构图。对于重新构造的图来说,如果新节点的入度是0,那么最优方法就是直接从s点向新节点添加一条边(贪心的思量,这样的结果最后必然是最优的),最后只需要注意s点所在的连通分量并不需要加边就行了。
关于tarjan缩点具体的解释可以参考这篇博客。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
int n, m, s;
const int maxn = 5005;
vector<int>G[maxn];
int belog[maxn], Stack[maxn], dfn[maxn], head[maxn], low[maxn], Instack[maxn];
int tot, top, Index, scc, indegree[maxn];
struct node {
int u, v, nxt;
} e[maxn];
void add_edge(int u, int v) {
e[tot].u = u;
e[tot].v = v;
e[tot].nxt = head[u];
head[u] = tot++;
}
void tarjan(int u) {
low[u] = dfn[u] = ++Index;
Instack[u] = 1;
Stack[top++] = u;
for(int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(Instack[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]) {
scc++;
int v;
do {
v = Stack[--top];
Instack[v] = 0;
belog[v] = scc;
} while(v != u);
}
}
int main() {
scanf("%d%d%d", &n, &m, &s);
memset(head, -1, sizeof(head));
for(int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
add_edge(u, v);
}
for(int i = 1; i <= n; i++) {
if(!dfn[i]) tarjan(i);
}
for(int i = 0; i < tot; i++) {//重新构图
int t1 = belog[e[i].u];
int t2 = belog[e[i].v];
if(t1 == t2) continue;
G[t1].pb(t2);
indegree[t2]++;
}
int ans = 0;
for(int i = 1; i <= scc; i++) {
if(indegree[i] == 0 && belog[s] != i) ans++;
}
printf("%d\n", ans);
return 0;
}
做法二:首先进行dfs对所有可以由s直接到达(不添加边)的点做一个标记。
然后对于其他需要添加边到达的点,同样进行dfs,很明显,如果节点u是某一个需要添加边的分量的树的根节点,最优解只需要从s添加一条边到这个根节点u就行了。最后只需要判断有几个这样的根节点就ok了。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn = 5005;
int n, m, s;
int vis[maxn];//vis[i]=1表示i点已经被访问过了
vector<int>G[maxn];
int flag[maxn];//flag[i]=1表示从s到i点又一条边
void dfs(int u) {
vis[u] = 1;
flag[u] = 0;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(!vis[v]) dfs(v);
}
}
int main() {
scanf("%d%d%d", &n, &m, &s);
for(int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].pb(v);
}
for(int i = 1; i <= n; i++) flag[i] = 1;
dfs(s);
for(int i = 1; i <= n; i++) {
if(flag[i] == 0) continue;
memset(vis,0,sizeof(vis));//注意清空,因为如果不清空的话们下次就会多产生要加的边
dfs(i);
flag[i] = 1;
}
int ans = 0;
for(int i = 1; i <= n; i++) {
ans += flag[i];
}
printf("%d\n", ans);
return 0;
}