P2835 刻录光盘

好久没写图论练练手。

这道题有两种解法:tarjan和并查集。我用的是tarjan缩点。

缩完点之后就只需要找出入度为0的新点有多少个就可以了。

md这道题卡快读???

代码:

#include<cstdio>
#include<algorithm>
const int maxn = 205;
struct Nodes
{
	int next, to;
} e[400005], e2[400005];
int head[maxn], tot;
int head2[maxn], tot2;
int dfn[maxn], low[maxn], dtot;
int stack[maxn], top;
int color[maxn], ctot;
bool vis[maxn];
bool b[maxn][maxn];
int indegree[maxn];

int n;

void link(int u, int v)
{
	e[++tot] = (Nodes){head[u], v};
	head[u] = tot;
}
void link2(int u, int v)
{
	e2[++tot2] = (Nodes){head2[u], v};
	head2[u] = tot2;
}
int read()
{
	int ans = 0, s = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
	return s * ans;
}
void tarjan(int u)
{
	dfn[u] = low[u] = ++dtot;
	stack[++top] = u; vis[u] = true;
	for(int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[u] = std::min(low[u], low[v]);
		}
		else if(vis[v]) low[u] = std::min(low[u], dfn[v]);
	}
	if(dfn[u] == low[u])
	{
		ctot++;
		while(stack[top + 1] != u)
		{
			int x = stack[top--]; vis[x] = false;
			color[x] = ctot;
		}
	}
}
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
	{
		int temp; scanf("%d", &temp);
		while(temp != 0)
		{
			link(i, temp);
			scanf("%d", &temp);
		}
	}
	for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
	for(int u = 1; u <= n; u++)
	{
		for(int i = head[u]; i; i = e[i].next)
		{
			int v = e[i].to;
			if(color[u] != color[v] && !b[color[u]][color[v]])
			{
				link2(color[u], color[v]);
				indegree[color[v]]++;
				b[color[u]][color[v]] = true;
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= ctot; i++)
	{
		if(indegree[i] == 0) ans++;
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2018-09-12 13:30  Garen-Wang  阅读(142)  评论(0编辑  收藏  举报