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;
}
posted @ 2020-05-12 18:02  shuitiangong  阅读(143)  评论(0编辑  收藏  举报