hdu 1827 Summer Holiday (tarjan + 缩点)
题目大意:
通知人需要一定的电话费,通知可以传递,给出m组关系,每组一个a一个b,表示a会打电话给b,问最少要通知多少个人,并且求最少的电话费
思路:
首先同tarjan求所有的强连通分量,然后缩点,找对应的强连通分量的点有几个入度为0,入度为0表示除了你没人会联系他,所以入度为0的点就是最小人数,然后再贪心的去找入度为0的点中电话费最小的,求和即可
入度为0的找法:
如果一个强连通分量内有一个点有一条指向另一个强连通分量内的一个点的边,则第一个强连通分量一定可以到大第二个强连通分量,因此可以将强连通分量缩成点以后重新建图
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define M(a) memset(a, 0, sizeof (a))
using namespace std;
const int N = 2010;
int st[N], dfn[N], low[N], vis[N], v[N], c[N], node[N], cost[N], head[N], nex[N], res[N], n, m, top, k, num, cnt;
void add(int a, int b){
	node[++k] = b;nex[k] = head[a];head[a] = k;
}
void init(){
	M(st), M(dfn), M(low), M(vis), M(v), M(c), M(node), M(head), M(nex);
	memset(res, 0x3f, sizeof (res));
	num = cnt = k = top = 0;
	for (int i = 1; i <= n; i++)scanf("%d", &cost[i]);
	for (int i = 1; i <= m; i++){int a, b;scanf("%d %d", &a, &b);add(a, b);}
}
void tarjan(int x){
	st[++top] = x;v[x] = 1;dfn[x] = low[x] = ++num;
	for (int i = head[x]; i; i = nex[i]){
		if(dfn[node[i]] == 0){
			tarjan(node[i]);
			low[x] = min(low[x], low[node[i]]);
		}
		else if(v[node[i]] != 0){
			low[x] = min(low[x], dfn[node[i]]);
		}
	}
	if(dfn[x] == low[x]){
		++cnt;
		while (top > 0){
			int t = st[top--];
			c[t] = cnt;
			v[t] = 0;//记录是否在栈内 
			if(t == x)break;
		}
	}
}
void numbers_of_inner(){
	for (int i = 1; i <= n; i++){
		res[c[i]] = min(res[c[i]], cost[i]);
		for (int j = head[i]; j; j = nex[j]){
			if(c[i] != c[node[j]])vis[c[node[j]]]++;
		}
	}
}
void solve(){
	init();
	for (int i = 1; i <= n; i++)if(dfn[i] == 0)tarjan(i);
	numbers_of_inner();
	int ans1 = 0, ans2 = 0;
	for (int i = 1; i <= cnt; i++){
		if(vis[i] == 0){
			++ans1;
			ans2 += res[i];
		}
	}
	printf("%d %d\n", ans1, ans2);
}
int main()
{
	while (~scanf("%d %d", &n, &m)){
		solve();
	}
	return 0;
}
本文来自博客园,作者:correct,转载请注明原文链接:https://www.cnblogs.com/correct/p/12862049.html

                
            
        
浙公网安备 33010602011771号