P1262 间谍网络

1、思路

阅读题目,发现有些间谍可以是被前面的点更新,也就是说,在一开始的时候,把能贿赂的人员从小到达排个序,再使用bfs算法,把他们能到达的人员的贿赂价钱设置为0。

有解的情况:

首先如果有环,我把环内的最少价钱的那一位买下,则整个环的间谍都被我买下。

首先把所有能被贿赂的根据bfs,依次把所有能到达的变为0

缩点之后,所有的都变为除了最小的那个,其他的都变为0,因此整个间谍网络只需要10就能买下。

但是对于如下情况

从小到大开始,依次是5,10,20,30。5能到达的点只有本身,不够,还要买下前面的环,而这个环10就能买下,因此如果有从X单向到Y,且X和Y都能买下,我们只需要买X。同理,尽管20比10更大,但是它还是需要买。否则无法买下所有的网络。因此,我们只需要买下缩点后入度为0的点就行。如果,如果入度为0的点不能被买下,那它就是无解。

有一个无法被遍历到,因此无解

无解的情况。

按能被贿赂的人员从小到大排序,依次处理每个能被贿赂的人员,如果能够被贿赂或者能够被更小的贿赂价钱到达,则打上一个标记,从小到大枚举所有人员,出现第一个标记则无解,输出该人员。

2、代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 6100, M = 11100;
struct PT //从小到大存储叛徒的价钱 
{
	int id,val;
	bool operator < (const PT &t)const 
	{
		return val < t.val;
	}
}pt[N];

int h[N],e[M],ne[M],idx;
int stk[N],top,timestamp;
bool st[N];
int dfn[N],low[N],id[N],Size[N],cnt;
int n,p,r;
int Value[N];
bool st1[N];
int din[N];
void add(int a,int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
void bfs(int x)// 将能到达的都置为0 
{

	if(st1[x]) return;
	st1[x] =true;
	int Q[N],tt = 1,hh = 0;
	Q[0] = x;
	while(hh!=tt)
	{
		int t = Q[hh++];
		for(int i = h[t];~i;i = ne[i])
		{
			int j = e[i];
			if(!st1[j])
			{
				st1[j] = true; 
				Q[tt++] = j;
				Value[j] = 0;
			}
		}
	}
}
void tarjan(int u) // 标准的tarjan算法 
{
	low[u] = dfn[u] = ++timestamp;
	stk[++top] = u;
	st[u] = true;
	for(int i = h[u];~i;i = ne[i])
	{
		int j = e[i];
		if(!dfn[j]) 
		{
			tarjan(j);
			low[u] = min(low[u],low[j]);
		}
		else if(st[j]) low[u] = min(low[u],dfn[j]);
	}
	if(low[u] == dfn[u])
	{
		++cnt;
		int y;
		do{
			y =  stk[top--];
			st[y] = false;
			id[y] = cnt;
			Size[cnt] += Value[y];
		}while(y!=u);
	}
}
int main()
{
	scanf("%d",&n);
	scanf("%d",&p);
	memset(h,-1,sizeof h);
	memset(Value,0x3f,sizeof Value);
	for(int i = 1;i<=p;i++)
	{
		scanf("%d%d",&pt[i].id,&pt[i].val);
		Value[pt[i].id] = pt[i].val;	
	}
	sort(pt+1,pt+p+1);
	
	scanf("%d",&r);
	for(int i = 1;i<=r;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);
	}
	for(int i = 1;i<=p;i++)
		bfs(pt[i].id);
			
	for(int i = 1;i<=n;i++)
	{
		if(!st1[i])
		{
			cout<<"NO"<<endl;
			cout<<i<<endl;
			return 0;
		}
	}
	for(int i = 1;i<=n;i++)if(!dfn[i])tarjan(i);
	for(int i = 1;i<=n;i++)
	{
		for(int j = h[i];~j;j = ne[j])
		{
			int k = e[j];
			int a = id[i],b = id[k];
			if(a!=b)din[b]++;
		}
	}
	int sum = 0;
	for(int i = cnt;i;i--)
	{
		 if(!din[i])sum += Size[i];
	}

	cout<<"YES"<<endl;
	cout<<sum<<endl;
	return 0;
} 
 

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值