题解 P1262 【间谍网络】

由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。

我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每个间谍分别用1到3000的整数来标识。

请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。

前置知识:

分析

会了强联通分量以后呢,我们可以开始写这道题了。首先这道题不是一个 D A G DAG 我们应该先缩点,把这个图变成一个 D A G DAG

变成 D A G DAG 就好了,我们就可以记录每个点的入度,答案即为所有入度为 0 0 的点的值的和(值就是这个点内的所有人被收买的数额中的最小值(不会被收买钱数为 inf \inf ))。

#include <bits/stdc++.h>
#define int long long
using namespace std;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar('0'+x%10);
}
const int MAXN=1e6+10,MAXM=1e6+10;
int s[MAXN],stop,dfn[MAXN],low[MAXN],scccnt,sccnum[MAXN],dfscnt,tot,he[MAXN],ne[MAXM<<1],ed[MAXM<<1],n,x,y,de[MAXN],ans,m,k,dis[MAXN],num[MAXN],sum[MAXN];
void add(int x,int y){
	ed[++tot]=y;
	ne[tot]=he[x];
	he[x]=tot;
}
inline void tarjan(int now){
	dfn[now] = low[now] = ++dfscnt;
	s[stop++] = now;
	for (int i=he[now];i;i=ne[i]){
		if(!dfn[ed[i]]){
			tarjan(ed[i]);
			low[now] = min(low[now], low[ed[i]]);
		}else if(!sccnum[ed[i]]) {
			low[now] = min(low[now], dfn[ed[i]]);
		}
	}
	if(dfn[now]==low[now]){
		scccnt++;
		do{
			sccnum[s[--stop]]=scccnt;
			sum[scccnt]=min(sum[scccnt],dis[s[stop]]);//取min
		}while(s[stop]!=now);
	}
}//强联通分量
signed main(){
	memset(sum,127,sizeof(sum));//赋上无穷大
	memset(dis,127,sizeof(dis));//赋上无穷大
	read(n);read(k);
	for(int i=1;i<=k;i++)read(num[i]),read(dis[num[i]]);
	read(m);
	for(int i=1;i<=m;i++){
		read(x);read(y);
		add(x,y);
	}
	for(int i=1;i<=k;i++)
		if(!dfn[num[i]])tarjan(num[i]);
	for(int i=1;i<=n;i++)
		if(!dfn[i])return cout<<"NO\n"<<i,0;//可以被收买的点中不知道这个点的消息,即为不可以控制所有间谍
	for(int i=1;i<=n;i++)
		for(int j=he[i];j;j=ne[j])
			if(sccnum[i]!=sccnum[ed[j]])de[sccnum[ed[j]]]++;
	for(int i=1;i<=scccnt;i++)
		if(!de[i])ans+=sum[i];
	cout<<"YES\n"<<ans;
	return 0;
}
发布了110 篇原创文章 · 获赞 188 · 访问量 7128

猜你喜欢

转载自blog.csdn.net/qq_46230164/article/details/105466156
今日推荐