HDU 1827 Summer Holiday(强连通分量,贪心)
解题思路
题目要求需要联系的最少人数和最小花费。先求出图中所有的连通分量,然后把他们都缩成一个点,这个点的花费就是同一连通分量中的最小的花费。对于缩点之后的图,找出所有入度为0的点开始就行了,为什么呢?因为没有点能够到达入度为0的点,所以对于每一个这样的点都需要访问一次。
代码
const int maxn = 1e4+10;
struct {
int to, nex;
} e[maxn<<1];
int h[maxn], ecnt;
void ae(int u, int v) {
e[ecnt].to = v;
e[ecnt].nex = h[u];
h[u] = ecnt++;
}
int dfn[maxn], dfncnt;
int low[maxn];
int sk[maxn], tp;
int scc[maxn], sc;
int cost[maxn], minc[maxn], in[maxn];
void tarjan(int u) {
sk[++tp] = u;
low[u] = dfn[u] = ++dfncnt;
for (int i = h[u]; i; i = e[i].nex) {
int v = e[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (!scc[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u]==low[u]) {
++sc;
while(true) {
int v = sk[tp--];
scc[v] = sc;
if (u==v) break;
}
}
return;
}
int main() {
int n, m, u, v;
while(~scanf("%d%d", &n, &m)) {
for (int i = 1; i<=n; ++i) {
dfn[i] = low[i] = scc[i] = in[i] = 0;
minc[i] = INF;
}
dfncnt = tp = sc = 0; ecnt = 1;
for (int i = 1; i<=n; ++i) scanf("%d", &cost[i]);
for (int i = 0; i<m; ++i) {
scanf("%d%d", &u, &v);
ae(u, v);
}
for (int i = 1; i<=n; ++i) if (!dfn[i]) tarjan(i);
for (int i = 1; i<=n; ++i)
minc[scc[i]] = min(minc[scc[i]], cost[i]);
for (int i = 1; i<=n; ++i)
for (int j = h[i]; j; j=e[j].nex)
if (scc[i]!=scc[e[j].to]) in[scc[e[j].to]] = 1;
int ans = 0, num = 0;
for (int i = 1; i<=sc; ++i)
if (!in[i]) {
++num;
ans += minc[i];
}
printf("%d %d\n", num, ans);
}
return 0;
}