hdu1827Summer Holiday(无向图缩点)

题目描述:

听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?

思路:我们要使得所有人都知道信息,图中的某些强联通分量就只需要通知一个人,其余人就都能知道了

所以我们对图进行缩点,一个强连通分量缩的点的花费就,取其中花费最小的那个,然后在从缩点后的图(不用真的再建图,只需统计点的入度)中

找到入读为0的点,这些点就是必须要通知的点,其余点都能被这些点传播到

之前做过一道几乎完全一样的题:间谍网络

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3100;
typedef long long ll;
struct edge {
    int f, t, nxt;
}e[maxn << 1];
int hd[maxn], tot;
void add(int f, int t) {
    e[++tot] = { f,t,hd[f] };
    hd[f] = tot;
}
int n, m;
int low[maxn], dfn[maxn],col[maxn],colcost[maxn], cost[maxn];
int cnt, colnum;
int stk[maxn], sk, instk[maxn];
int du[maxn];
void init() {
    tot = cnt = colnum = 0;
    memset(hd, 0, sizeof(hd));
    memset(dfn, 0, sizeof(dfn));
    memset(col, 0, sizeof(col));
    memset(colcost, 0x3f, sizeof(colcost));
    memset(du, 0, sizeof(du));
}
void dfs(int u) {
    low[u] = dfn[u] = ++cnt;
    stk[++sk] = u; instk[u] = 1;
    for (int i = hd[u]; i; i = e[i].nxt) {
        int v = e[i].t;
        if (!dfn[v]) {
            dfs(v);
            low[u] = min(low[u], low[v]);
        }
        else if (instk[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (low[u] == dfn[u]) {
        colnum++;
        while (1) {
            int v = stk[sk--];
            instk[v] = 0;
            col[v] = colnum;
            colcost[colnum] = min(colcost[colnum], cost[v]);
            //一个联通分量只需要取需要电话费最少的那个人就可以了
            if (v == u)break;
        }
    }
}
int main() {
    //freopen("test.txt", "r", stdin);
    while (~scanf("%d%d", &n, &m)) {
        init();
        for (int i = 1; i <= n; i++){
            scanf("%d", &cost[i]);
        }
        while (m--) {
            int a, b; scanf("%d%d", &a, &b);
            add(a, b);
        }
        for (int i = 1; i <= n; i++) {
            if (!dfn[i]) {
                dfs(i);
            }
        }
        for (int i = 1; i <= tot; i++) {//重新建图,不用真的建,只需要标记入读,
            int u = e[i].f, v = e[i].t;
            if (col[u] != col[v]) {//两个点不在一个连通分量里面
                du[col[v]]++;
            }
        }
        ll ans = 0,num=0;
        for (int i = 1; i <= colnum; i++) {
            if (du[i] == 0) {//度为0,说明是必须要告诉的,不然他就没法知道信息
                num++;
                ans += colcost[i];
            }
        }
        printf("%lld %lld\n", num,ans);
    }
    return 0;
}

 

posted @ 2021-04-02 18:23  cono奇犽哒  阅读(66)  评论(0)    收藏  举报