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; }