[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\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 }
劍的意義……會根據役劍之人而改變…… 你要役劍……絕不能……為劍所役…… 你要自由的活下去……絕不能被家名拘束…… 什麽才是正確的……你必須,用你的雙眼去證實……
—— 黑羽《穢翼的尤斯蒂婭》
[Luogu 1262] 間諜網絡