【计蒜客 - 程序设计竞赛】商业信息共享(Tarjan缩点)

本文探讨了如何通过最少数量的公司传播商业信息,以及如何通过增加最少的合作关系使信息能在所有公司间流通。使用Tarjan算法进行节点压缩处理,解决了寻找最小传播源及构建强联通图的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题干:

商业信息共享

有 N 个公司,从每个公司都能单向地向另外一个公司分享最新商业信息,因为他们之间有着某种合作,你需要解决两个问题:

现在有一个最新的商业信息,至少需要告诉多少个公司,使得所有的公司最终都能得到该信息。
在原有基础上,至少需要再让多少对公司建立这种合作,使任意一个公司获得某个最新商业信息后,经过若干次分享,所有的公司最终都能得到该信息。
输入格式
第一行输入一个整数 N (1≤N≤100)。

接下来 N行,每行若干个整数,表示第 ii 个公司可以向哪些公司分享信息,以 0 结束。

输出格式
输出共两行,每行一个整数,分别表示问题 1 和问题 2 的答案。

样例输入
6
0
6 0
2 0
2 0
3 1 0
0
样例输出
2
2

题目大意:

两个要求,

1.用最少的公司,将所有的信息传递给其他的公司。

2.最少加多少条边,使图变成强联通图。

解题报告:

又是“传递关系”的问题,还是用tarjan缩点做。

对于1小问,其实就是运用性质①,找入度为0的超级点(你自己联想线性序列嘛!其实就是求这里有几坨线性序列)

对于第2小问也是以“超级点”来做的:

需要思考一下,首先你要知道超级点其实就是一个强联通分量(涵盖的点互相可达),那么对于每个超级点,如果既有边指向它,它又可以指出去,如果每个点都这样“开放 既可以get in又可以get out”,那其实就满足题意说的“任意一个点发消息可达所有点”。否则你想想,如果入度为0了,那么这个超级点只能get out,别人访问不了你啊!同理,出度为0,你访问不了别人啊!这两种情况都会不符合题意的。所以我们就需要找到有没有超级点是出/入度为0的,如果你入度为0,那么我需要给你随便连一条边进来好让别人能够get in,如果你出度为0,那么我需要给你随便连一条边出去好让你可以get out。

因为我连一条线可以解决同时解决一个出度为0和一个入度为0的问题,如果只剩出度为0的问题了,就任意给它连出去就好了,因此,max(出度=0的点数,入度=0的点数)即为所求。

AC代码:(没地方交,不知道正确与否)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
int n,m;y
int head[MAX];
int DFN[MAX],LOW[MAX],col[MAX],cnt[MAX],stk[MAX],out[MAX];
bool vis[MAX];
struct Edge {
	int fr,to,ne;
} e[MAX],ee[MAX];
int tot,tot2,timing,scc,index;
void add(int u,int v) {
	e[++tot].fr = u;
	e[tot].to = v;
	e[tot].ne = head[u];
	head[u] = tot;
}
void Tarjan(int x) {
	LOW[x] = DFN[x] = ++timing;
	vis[x] = 1;
	stk[++index] = x;
	for(int i = head[x]; i!=-1; i=e[i].ne) {
		int v = e[i].to;
		if(!DFN[v]) {
			Tarjan(v);
			LOW[x] = min(LOW[x],LOW[v]);
		}
		else if(vis[v]) LOW[x] = min(LOW[x],DFN[v]);
	}
	if(DFN[x] == LOW[x]) {
		scc++;
		while(1) {
			int tmp = stk[index];index--;
			vis[tmp]=0;
			col[tmp] = scc;
			cnt[scc]++;
			if(x == tmp) break;
		}
	}
}
int main()
{
	cin>>n;
	memset(head,-1,sizeof head);
	for(int a,b,i = 1 ; i<=n; i++) {
		while(scanf("%d",&b) && b) {
			add(i,b);
		}
	}
	for(int i = 1; i<=n; i++) {
		if(!DFN[i]) Tarjan(i);
	}
	for(int i = 1; i<=m; i++) {
		if(col[e[i].fr] != col[e[i].to]) {
			out[col[e[i].fr]]++;
			in[col[e[i].to]]++;
		}
	}
	int cntin=0,cntout=0;
	for(int i = 1; i<=scc; i++) {
		if(out[i] == 0) cntout++;
		if(in[i] == 0) cntin++;
	}
	printf("%d\n",cntin);
	printf("%d\n",max(cntin,cntout));
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值