算法刷题记录(Day 25)

Popular Cows(poj 2186)

原题链接
解题思路:B是受欢迎的,当且仅当所有的点都能够到达B,因此对于每一个关系,可以建立反向边,将每一个点都能够到达B转变为从B出发能够到达所有的点。对于每一个点,进行深度优先遍历或者广度优先遍历,看是否能到达所有的点,由此判断其是否是受欢迎的。

参考思路1:不是很能理解为什么说一定会转化为一棵有向树

有向树:
满足下列条件的有向图被称为有向树:
(1)有且仅有一个结点的入度为0;
(2)除树根外的结点入度为1;
(3)从树根到任一结点有一条有向通路

参考思路2:建立反向边。使用tarjan算法缩点之后,去寻找入度为0的点,若该种点的个数大于1,则不存在,否则为入度为0的点所代表的联通分量的点的个数。
在tarjan算法的求解过程中,可以去记录该id对应的联通分量的点的个数。
对于度的统计,需要注意的是,若两者位于同一个联通分量中,则不能将to点所在的连通分量的度加上1。同时,在本题中,涉及度的判断仅仅为是否为0值,因此对于度的计算是错误的。若A,B(A,B都属于id1联通分量)都含有到C(C属于id2联通分量)的边,则id2的度应该只能计算一次。

tarjan算法例子
tarjan算法概念性描述
dfn:代表的是点被访问的次序
low:代表的是该点能够到达的栈中的点的位置,low值得传播是后向的,即若存在一条从u到v得边,那么u的low值需要v来进行更新。(由low值来体现强联通的特性)

//1216K	360MS
#include<iostream>
#include<stack>
#include<vector>
#include<cstring>
using namespace std;
#define NMAX 11000
int N, M;
int a, b;
struct edge {
	int from, to, next;
};
vector<edge> E;
stack<int > S;
int e = 0;
int h[NMAX];
//tarjan所需要的变量
int dfn[NMAX], low[NMAX];//low也代表了点i能够到达栈中的哪一个点,并由此传递下去,又因为栈前面的点一定是能到达i的,因此是一个强连通分量
int tot = 0, id = 0;//分别代表的是点被tarjan遍历到的次序,id代表的是查找到的强连通分量的标号
int vis[NMAX];
int belong[NMAX];
int Num[NMAX];//用于统计每一个编号为id的联通分量含有多少个顶点
int du[NMAX];
void tarjan(int x) {
	low[x] = dfn[x] = ++tot;
	//标记vis
	vis[x] = 1;
	S.push(x);
	//遍历x联通的每一个点
	for (int i = h[x]; i != -1; i = E[i].next) {
		if (!dfn[E[i].to]) {
			tarjan(E[i].to);
			low[x] = min(low[x], low[E[i].to]);
		}
		else if (vis[E[i].to]) low[x] = min(low[x], dfn[E[i].to]);
	}

	//进行强联通分量的查找
	if (low[x] == dfn[x]) {
		++id;
		int now = 0;
		Num[id] = 0;
		while (now != x) {
			now = S.top();
			S.pop();
			vis[now] = 0;
			belong[now] = id;
			Num[id]++;
		}
	}
}
int main() {
	while (cin >> N >> M) {
		memset(h, -1, sizeof(h));
		E.clear();
		for (int i = 0; i < M; i++) {
			cin >> a >> b;
			struct edge cur;
			cur.to = a, cur.next = h[b], cur.from = b;
			h[b] = E.size();
			E.push_back(cur);
		}
		
		//初始化tanjan所需要使用的变量
		tot = 0, id = 0;
		memset(dfn, 0, sizeof(dfn));
		memset(low, 0, sizeof(low));
		memset(vis, 0, sizeof(vis));
		memset(du, 0, sizeof(du));
		for (int i = 1; i <= N; i++) {
			if (!dfn[i]) tarjan(i);
		}

		//计算入度
		for (int i = 0; i < E.size(); i++) {
			if(belong[E[i].from]!=belong[E[i].to]) du[belong[E[i].to]]++; //强联通分量内部是不包括在内的
		}
		//构建新的图
		int res = 0;
		int ret;
		for (int i = 1; i <= id; i++) {
			if (!du[i]) {
				res++;
				ret = i;
			}
		}
		if (res == 1) cout << Num[ret] << endl;
		else cout << 0 << endl;
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值