poj1236 Network of Schools tarjan算法

http://poj.org/problem?id=1236

题意:给你一个有向图,1、求最少给多少的点发信息,可以使得所有的点都可以得到信息,2、求最少加上多少条边后,可以使图强连通。

题解:1、跑完tarjan算法后,将每个强连通分量缩点,然后求出入度为0的缩点的个数,这也是最小点基的定义。2、最少加上多少条边后,可以使图强连通。可以发现找出入度为0和出度为0的缩点个数a,b。若a>b,可以从每个入度为0的点向出度为0的点连边,反之亦然。这样保证了强连通。

另:需对本就是强连通的图特判,因为a=b=1。

代码:

#include<set>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<bitset>

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>

#include<iomanip>
#include<iostream>

#define debug cout<<"aaa"<<endl
#define d(a) cout<<a<<endl
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define MIN_INT (-2147483647-1)
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807i64
#define MIN_LL (-9223372036854775807i64-1)
using namespace std;

const int N = 100 + 5;
const int mod = 1000000000 + 7;
const double eps = 1e-8;
int head[N],len;
int dfn[N],low[N],dfs_num;//dfn表示遍历深度,low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号
int color[N],col_num;//染色 
int stack[N],vis[N],top;//栈和栈指针
int outd[N],ind[N];
int ans1,ans2; 

struct EdgeNode{
	int from,to,next;
}edge[N*N];

void add(int i,int j){
	edge[len].from=i;
	edge[len].to=j;
	edge[len].next=head[i];
	head[i]=len++;
} 

void init(int n){
	mem(outd,0),mem(ind,0),mem(vis,0),mem(dfn,0),mem(low,0),mem(head,-1),len=ans1=ans2=top=col_num=dfs_num=0;
}

void tarjan(int x){
	dfn[x]=++dfs_num;
	low[x]=dfs_num;
	vis[x]=1;//是否在栈中
	stack[++top]=x;
	for(int i=head[x];i!=-1;i=edge[i].next){
		int temp=edge[i].to;
		if(!dfn[temp]){
			tarjan(temp);
			low[x]=min(low[x],low[temp]);
		}
		else if(vis[temp]){
			low[x]=min(dfn[temp],low[x]);
		}
	} 
	if(dfn[x]==low[x]){//构成强连通分量 
		vis[x]=0;
		//染色 
		color[x]=++col_num; 
		while(stack[top]!=x){//退栈 
			color[stack[top]]=col_num;
			vis[stack[top]]=0;
			top--; 
		}
		top--;
	}
}

void solve(int n){
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tarjan(i);
		}
	}
	//计算入度并缩点 
	for(int i=0;i<len;i++){
		if(color[edge[i].from]!=color[edge[i].to]){
			ind[color[edge[i].to]]++;
			outd[color[edge[i].from]]++;
		}
	}
	for(int i=1;i<=col_num;i++){
		//若入度为0,则加入最小权值 
		if(ind[i]==0){
			ans1++;
		}
		if(outd[i]==0){
			ans2++;
		}
	} 
}

int main(){
	int n,m,u,v;
	while(~scanf("%d",&n)){
		init(n);
		for(int i=1;i<=n;i++){
			while(scanf("%d",&v)&&v){
				add(i,v);	
			}
		}
		solve(n);
		if(col_num==1){
			puts("1");
			puts("0");
			continue;
		}
		printf("%d\n",ans1);
		printf("%d\n",max(ans1,ans2));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值