[Luogu 1262] 间谍网络

题目描述

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

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

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

输入输出格式

输入格式:

 第一行只有一个整数n。

 第二行是整数p。表示愿意被收买的人数,1≤p≤n。

 接下来的p行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过20000。

 紧跟着一行只有一个整数r,1≤r≤8000。然后r行,每行两个正整数,表示数对(A, B),A间谍掌握B间谍的证据。

输出格式:

 如果可以控制所有间谍,第一行输出YES,并在第二行输出所需要支付的贿金最小值。否则输出NO,并在第二行输出不能控制的间谍中,编号最小的间谍编号。

输入输出样例

输入样例#1:
3
2
1 10
2 100
2
1 3
2 3

输出样例#1:

YES
110
 
输入样例#2:
4
2
1 100
4 200
2
1 2
3 4

输出样例#2:

NO
3

My Solution :

还不是很懂 Tarjan 缩点的戳这里w -> 简要了解 Tarjan 缩点!

感觉很神奇的一道题,让我仅仅是按照思路实现一遍代码就用了一个小时,结果没过样例;又调试了半个小时才过。

涉及到以下步骤:

 1、染色缩点:Tarjan 染色缩点同时处理连通块个数(很简单而且个数这个好像没卵用);

 2、重新建图:按照缩点后的图重新建图,并统计新图的入度,以便于之后的搜索(之前做过的题都没必要有这个步骤……可能那就是裸题吧);

 3、初始化花费:若一个点(以下提到的均为缩点后的点,即连通块)中有可收买的间谍,则这个点可控制,将其收买的价值 cost[](初值为 0x3f3f3f3f)取 min 更新,为收买它要用的最少金额;

 4、深搜更新:更新从一个点 u 能到达的点 v 即 v 能被 u 所控制,故从入度为零的点开始 Dfs,更新每个点被控制所需的最小价格(记得过程中也要取 min,因为也许始点不能被控制而路径上出现了能被收买的点,不取 min 会影响第 5 步中的判断,详见代码,这里让我WA了一个点);

 5、枚举判断:最后枚举所有间谍,查询 cost[ col[i] ](即控制其所在连通块的最小价值)是否仍为 0x3f3f3f3f,如果是,则此间谍无法被控制,GG;否则输出 ans;

 6、输出答案:关于 ans,即收买最小金额的统计,因为入度为 0 的点只能进行收买而无法被控制,所以 ans 即为第 3 步之后,所有入度为零的点的 cost[] 之和,在第 4 步中一起统计好即可。

放个杂乱的代码,Tarjan 真是太有用了:

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cctype>
 4 #include <cstring>
 5 #include <iostream>
 6 #include <algorithm>
 7 using namespace std;
 8 
 9 const int maxn = 3000 + 10;
10 const int maxm = 8000 + 10;
11 int n, m, p, head[maxn], dfn_num, col_num, edge_num;
12 int buy[maxn], val[maxn], deg[maxn], cost[maxn], con[maxn];
13 int dfn[maxn], low[maxn], vis[maxn], col[maxn], cnt[maxn], stack[maxn], top;
14 
15 struct Edge{ int u, v, nxt; }edge[maxm];
16 
17 inline int read() {
18   register char ch = 0; register int w = 0, x = 0;
19   while( !isdigit(ch) ) w |= (ch == '-'), ch = getchar();
20   while( isdigit(ch) ) x = (x * 10) + (ch ^ 48), ch = getchar();
21   return w ? -x : x;
22 }
23 
24 inline void Add_edge(int u, int v) {
25   edge[++edge_num].u = u, edge[edge_num].v = v;
26   edge[edge_num].nxt = head[u], head[u] = edge_num;
27 }
28 
29 void Tarjan(int s) {
30   dfn[s] = low[s] = ++dfn_num;
31   vis[s] = 1, stack[++top] = s;
32   for(int i = head[s]; i; i = edge[i].nxt) {
33     if( !dfn[edge[i].v] ) {
34       Tarjan(edge[i].v), low[s] = min(low[s], low[edge[i].v]);
35     } else if( vis[edge[i].v] ) low[s] = min(low[s], dfn[edge[i].v]);
36   }
37   if( dfn[s] == low[s] ) {
38     col[s] = ++col_num, cnt[col_num] = 1;
39     while( stack[top] != s ) {
40       col[stack[top]] = col_num, ++cnt[col_num];
41       vis[stack[top]] = 0, --top;
42     }
43     vis[s] = 0, --top;
44   }
45 }
46 
47 void Deep_fs(int x, int price) {
48   for(int i = head[x]; i; i = edge[i].nxt) {
49     cost[edge[i].v] = min(cost[edge[i].v], price);
50     Deep_fs(edge[i].v, min(cost[edge[i].v], price));
51   }
52 }
53 
54 inline void Failed(int x) { printf("NO
%d
", x), exit(0); }
55 
56 int main(int argc, char const *argv[]) 
57 {
58   freopen("nanjolno.in", "r", stdin);
59   freopen("nanjolno.out", "w", stdout);
60 
61   int u = 0, v = 0, ans = 0;
62   scanf("%d%d", &n, &p);
63   for(int i = 1; i <= p; ++i) buy[i] = read(), val[i] = read();
64   scanf("%d", &m);
65   for(int i = 1; i <= m; ++i) u = read(), v = read(), Add_edge(u, v);
66   for(int i = 1; i <= n; ++i) if( !dfn[i] ) Tarjan(i);
67 
68   // for(int i = 1; i <= n; ++i) printf("%d ", col[i]);
69   // printf("
");
70 
71   memset(head, 0, sizeof head), edge_num = 0;
72   for(int i = 1; i <= m; ++i)
73     if( col[edge[i].u] != col[edge[i].v] )
74       Add_edge(col[edge[i].u], col[edge[i].v]), ++deg[edge[edge_num].v];
75 
76   // for(int i = 1; i <= col_num; ++i) printf("%d ", deg[i]);
77   // printf("
");
78 
79   memset(cost, 0x3f, sizeof cost);
80   for(int i = 1; i <= p; ++i)
81     cost[col[buy[i]]] = min(cost[col[buy[i]]], val[i]);
82   for(int i = 1; i <= col_num; ++i)
83     if( !deg[i] ) Deep_fs(i, cost[i]), ans += cost[i];
84 
85   // for(int i = 1; i <= col_num; ++i) printf("%d ", cost[i]);
86   // printf("
");
87 
88   for(int i = 1; i <= n; ++i) 
89     if( cost[col[i]] == 0x3f3f3f3f ) Failed(i);
90   printf("YES
%d
", ans);
91   
92   fclose(stdin), fclose(stdout);
93   return 0;
94 }
 剑的意义……会根据役剑之人而改变……
 你要役剑……绝不能……为剑所役……
 你要自由的活下去……绝不能被家名拘束……
 什么才是正确的……你必须,用你的双眼去证实……

                      —— 黑羽《秽翼的尤斯蒂娅》

原文地址:https://www.cnblogs.com/nanjoqin/p/9472315.html