P1262 间谍网络

题目传送门

描述:(给你一个有向图,其中某些点是可以买的,如果你买了一个点,)

(你可以到达这个点指向的点,指向的这个点又能到它指向的点......(直到走不通),我们要选择一些点买,使图联通且代价最小!)

算法:tarjan缩点

思考过程

选择哪一些点,能使图联通且代价最小呢?

我们试着想一下,如果一个点被其他点指向,那我们最好选那个点

如果那个点也被其他点指向,我们一直追溯过去,入度为0的点必须要选了

但是如果存在环,那么没有点入度为0,这个时候就需要缩点,变成我们上面的那种情况

缩点后环的代价就是环中点的最小代价

问题又来了,如果环中没有点可以买怎么办??

所以tarjan的时候只对那些可以买的点进行找环

最后扫一遍,如果有点的dfn为0,说明没有遍历,即它是孤立的,没办法买到的,输出NO~

#include <bits/stdc++.h>
using namespace std;
const int maxn=16009;
const int inf=999999999;
int n,m,q;
struct p{
	int to,nxt,w;
}d[maxn];int head[maxn],cnt=1;
void add(int u,int v,int w){
	d[cnt].to=v,d[cnt].w=w,d[cnt].nxt=head[u],head[u]=cnt++;
}
int indug[maxn],monney[maxn],sumn[maxn];
int low[maxn],dfn[maxn],stac[maxn],vis[maxn],top,id,sd[maxn],fen;
void tarjan(int now)
{
	dfn[now]=low[now]=++id;
	stac[++top]=now,vis[now]=1;
	for(int i=head[now];i;i=d[i].nxt)
	{
		int v=d[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[now]=min(low[now],low[v]);
		}
		else if(vis[v])	low[now]=min(low[now],low[v]);
	}
	if(dfn[now]==low[now])
	{
		fen++;int temp;
		while(temp=stac[top--])
		{
			vis[temp]=0;
			sd[temp]=fen;
			sumn[fen]=min(sumn[fen],monney[temp]);
			if(temp==now)	break;
		}
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)	monney[i]=sumn[i]=inf;
	for(int i=1;i<=m;i++)
	{
		int l,r;cin>>l>>r;
		indug[l]++;monney[l]=r;
	}
	cin>>q;
	for(int i=1;i<=q;i++)
	{
		int l,r;cin>>l>>r;
		add(l,r,0);indug[r]++;
	}
	for(int i=1;i<=n;i++)
		if(indug[i]==0)
		{
			cout<<"NO"<<endl<<i;
			return 0;
		}
	for(int i=1;i<=n;i++)//开始缩点
	if(!dfn[i]&&monney[i]!=-1)	tarjan(i);
	memset(indug,0,sizeof(indug));
	for(int i=1;i<=n;i++)
		for(int j=head[i];j;j=d[j].nxt)
		{
			int v=d[j].to;
			if(sd[i]==sd[v])	continue;
			indug[sd[v]]++;
		}
	int ans=0;
	for(int i=1;i<=fen;i++)
	if(!indug[i])	ans+=sumn[i];
	cout<<"YES"<<endl<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/iss-ue/p/12579837.html