[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,并在第二行输出不能控制的间谍中,编号最小的间谍编号。
输入输出样例
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\n%d\n", 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("\n"); 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("\n"); 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("\n"); 87 88 for(int i = 1; i <= n; ++i) 89 if( cost[col[i]] == 0x3f3f3f3f ) Failed(i); 90 printf("YES\n%d\n", ans); 91 92 fclose(stdin), fclose(stdout); 93 return 0; 94 }
—— 黑羽《秽翼的尤斯蒂娅》