洛谷:间谍网络
很清晰明了的一道 考察 强连通缩点转拓扑序 的题目
需要维护的信息:
- 是否能控制全部间谍
- 最少的贿赂花费
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
*/