luoguP1262 间谍网络 (有向图的强连通分量+缩点tajan算法)

原题链接

对于有向图的强连通分量tarjan算法解析请看这篇文章

本题属于强连通分量的缩点的偏模板题,可以将情况分为两种,一是含有无法贿赂无法揭发的罪犯即输出NO+罪犯编号
二是 1、罪犯揭发链不成环,那么需要入度为0的罪犯的钱,2、如果成环那么就需要环中最小的钱,对于2我们经过环的缩点可以转化为1,所以就是找缩点后入度为0的点答案想加便可!!详细请看代码及注释

#include<iostream>  
#include<cstring>  
#include<cstdio>  
#include<algorithm>
using namespace std;
const int MAX_N = 100000;
#define N 100000  
#define M 100000
#define inf 0x3f3f3f3f
int tot = 0;
int head[MAX_N], nxt[MAX_N*2], ver[MAX_N *2], wei[MAX_N *2];
void addedge(int u, int v)
{
	nxt[++tot] = head[u], head[u] = tot, ver[tot] = v;
}
int instack[N];//是否在stack  
int stack[N];
int Belong[N];//各顶点属于哪个强连通分量  
int DFN[N];//时间戳  
int LOW[N];//u或u的子树能够追溯到的最早的栈中节点的序号(时间戳)  
int n, m;//n:点的个数;m:边的条数  
int cnt_edge;//边的计数器 
int Index;//时间戳
int top;
int Bcnt;//有多少个强连通分量  
int mny[MAX_N];//记录每个可贿赂的点的钱
int sdmny[MAX_N];//缩点时记录环中最少的钱
int rd[MAX_N];//记录缩点后每个点的入度,方便找入度为0的点

void tarjan(int u)//tartjan缩点
{
	int i, j;
	int v;
	DFN[u] = LOW[u] = ++Index;
	instack[u] = true;
	stack[++top] = u;
	for (i = head[u]; i != -1; i = nxt[i])
	{
		v = ver[i];
		if (!DFN[v])//如果点v没被访问  
		{
			tarjan(v);
			if (LOW[v] < LOW[u])
				LOW[u] = LOW[v];
		}
		else//如果点v已经被访问过  
			if (instack[v] && DFN[v] < LOW[u])
				LOW[u] = DFN[v];
	}
	if (DFN[u] == LOW[u])
	{
		Bcnt++;
		do
		{
			j = stack[top--];
			instack[j] = false;
			Belong[j] = Bcnt;
			sdmny[Bcnt] = min(sdmny[Bcnt], mny[j]);//缩点时记录最小钱
		} while (j != u);
	}
}
void solve()
{
	int i;
	top = Bcnt = Index = 0;
	memset(DFN, 0, sizeof(DFN));
	memset(LOW, 0, sizeof(LOW));
	for (i = 1; i <= n; i++)
		if (!DFN[i] && mny[i] != inf)
		{
			tarjan(i);
		}
			
}
int main()
{
	memset(mny, inf, sizeof(mny));
	memset(sdmny, inf, sizeof(sdmny));
	memset(head, -1, sizeof(head));
	int p;
	scanf("%d", &n);
	scanf("%d", &p);
	for (int i = 1; i <= p; i++)
	{
		int t1, t2;
		scanf("%d %d", &t1, &t2);
		mny[t1] = t2;
	}
	scanf("%d", &m);
	for (int i = 1; i <= m; i++)
	{
		int t1, t2;
		scanf("%d %d", &t1, &t2);
		addedge(t1, t2);
	}
	solve();
	for (int i = 1; i <= n; i++)
	{
		if (!DFN[i])
		{
			printf("NO\n");
			printf("%d\n", i);
			return 0;
		}
	}
	for (int i = 1;i <= n; i++)
	{
		for (int j = head[i]; j != -1; j = nxt[j])
		{
			if (Belong[i] != Belong[ver[j]])
			{
				rd[Belong[ver[j]]]++;
			}
		}
	}
	printf("YES\n");
	int ans = 0;
	for (int i = 1; i <= Bcnt; i++)
	{
		if (!rd[i])
		{
			ans += sdmny[i];
		}
	}
	printf("%d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shihao Weng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值