洛谷:P1262 间谍网络(强连通缩点 + 拓扑序维护信息)

洛谷:间谍网络

在这里插入图片描述

很清晰明了的一道 考察 强连通缩点转拓扑序 的题目

需要维护的信息:

  1. 是否能控制全部间谍
  2. 最少的贿赂花费

1:强连通缩点转拓扑序列维护信息即可
2:贪心,缩点时维护每个连通块的最小间谍贿赂,显然最少花费只花在入度为 0 的连通块上

综合一下即可(结果还是被卡了

代码:

#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define fx first
#define fy second
//#pragma GCC optimize(2)
//[博客地址](https://blog.csdn.net/weixin_51797626?t=1) 
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

const int N = 3010, M = 20010, MM = 3000010;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int h[N], rh[N], e[M], ne[M], idx;
int dfn[N], low[N], tim;
int stk[N], top;
bool v[N], st[N];
int scc_cnt, id[N], siz[N], cost[N], d[N];
int ru[N], ans;

struct edge
{
	int a, b;
}ed[M];

void add(int* h, int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void tarjan(int x) {
	dfn[x] = low[x] = ++tim;
	stk[++top] = x;
	v[x] = true;
	for (int i = h[x]; ~i; i = ne[i]) {
		int j = e[i];
		if (!dfn[j]) {
			tarjan(j);
			low[x] = min(low[x], low[j]);
		}
		else if (v[j])low[x] = min(low[x], dfn[j]);
	}

	if (dfn[x] == low[x]) {
		int y;
		scc_cnt++;
		do {
			y = stk[top--];
			v[y] = false;
			id[y] = scc_cnt;
			siz[scc_cnt]++;
			cost[scc_cnt] = min(cost[scc_cnt], d[y]);
			//维护一个连通块内最小的间谍被收买资金

		} while (x != y);
	}
}

bool top_sort() {
	queue<int> q;
	for (int i = 1; i <= scc_cnt; i++) {
		if (cost[i] != INF)st[i] = true;//能收买的间谍都能传递情报
		//不能只让入度为 0 的间谍传递,会遗漏信息
		//这也是被阴间数据卡的原因
		if (!ru[i]) {
			q.push(i);
			if (cost[i] != INF)ans += cost[i];//支付最少的资金
			//显然只支付给入度为 0 的间谍
		}
	}

	int cnt = 0;//统计被控制的间谍数
	while (q.size())
	{
		int t = q.front();
		q.pop();

		if (st[t])cnt += siz[t];
		//只有被标记过的间谍才代表可以被控制

		for (int i = rh[t]; ~i; i = ne[i]) {
			int j = e[i];
			st[j] |= st[t];//信息传递
			if (--ru[j] == 0)q.push(j);
		}
	}

	return cnt == n;
}

int main() {
	cinios;

	cin >> n >> m;
	mem(h, -1), mem(rh, -1);
	mem(cost, 0x3f);//初始化
	mem(d, 0x3f);

	while (m--)
	{
		int a, b;
		cin >> a >> b;
		d[a] = min(d[a], b);
	}

	cin >> m;
	for (int i = 0; i < m; i++)
	{
		int a, b;
		cin >> a >> b;
		add(h, a, b);
		ed[i] = { a,b };
	}

	for (int i = 1; i <= n; i++)
		if (!dfn[i])tarjan(i);

	for (int i = 0; i < m; i++)
	{
		int a = ed[i].a, b = ed[i].b;
		if (id[a] != id[b]) {
			add(rh, id[a], id[b]);
			ru[id[b]]++;
		}
	}

	if (top_sort())cout << "YES" << endl << ans;
	else {
		cout << "NO" << endl;//无法控制全部间谍
		for (int i = 1; i <= n; i++)
			if (!st[id[i]]) { //输出第一个没被标记的间谍
				cout << i;
				break;
			}
	}

	return 0;
}
/*
阴间数据
in:
9
2
1 100
9 10000
9
1 3
4 3
2 4
2 1
8 5
8 7
5 6
7 6
4 5

out:
NO
2
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值