1. 程式人生 > >「洛谷P1262」間諜網路 解題報告

「洛谷P1262」間諜網路 解題報告

P1262 間諜網路

題目描述

由於外國間諜的大量滲入,國家安全正處於高度的危機之中。如果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

演算法

縮點

思路

如果A掌握B的資訊,那麼我們就建有向邊A->B,構成一個圖。
每個點的權值就是收買該間諜需要花的錢,如果不能收買,那就賦為\(\infty\)
我們的任務就是取Sum(點權)最少的若干個點,以這幾個點為起點,能遍歷整張圖。
當然,Sum(點權)不能為\(\infty\)。(注意下 \(\infty + \infty = \infty \ \ ,\infty + x = \infty\)

因為在一個強連通分量中,任何一個點都能遍歷到每個節點。所以,對於一個強連通分量,完全可以看成一個點,點權為該強連通分量包含的點的最小點權。

所以縮點即可。以下所說的點均為“縮”過的點。

入度為0的點絕對要取,而取來後絕對已經所有點都能訪問到。

這個很好證明。入度為0的點絕對要取是不難理解的,因為它不可能被其他點訪問。由於縮點後的圖不可能構成環,所以入度不為0的點肯定可以被已經訪問過的點訪問到。

注意下不能被控制的(編號最小)點不一定入度為0。

如這組資料:

Input Data

4
1
4 100
4
2 4
2 1
4 3
1 3

Output Data

NO
1

程式碼

#include<bits/stdc++.h>
using namespace std;
#define MAXN 3005
#define MAXM 8005

int n, p, r;
int hd[MAXN], nxt[MAXM], to[MAXM], tot(1);
int dfn[MAXN], low[MAXN], num, stk[MAXN], tp, c[MAXN], cnt;
int pr[MAXN], f[MAXN];
int x, y;
bool vs[MAXN], v[MAXN];

void Add( int x, int y){ nxt[++tot] = hd[x]; hd[x] = tot; to[tot] = y; }

void Tarjan( int x ){
    dfn[x] = low[x] = ++tot; stk[++tp] = x;
    for ( int i = hd[x]; i; i = nxt[i] ){
        if ( !dfn[to[i]] ) Tarjan( to[i] ), low[x] = min( low[x], low[to[i]] );
        else if ( !c[to[i]] ) low[x] = min( low[x], dfn[to[i]] );
    }
    if ( low[x] == dfn[x] ){
        c[x] = ++cnt; f[cnt] = pr[x];
        while( stk[tp] != x ) f[cnt] = min( f[cnt], pr[stk[tp]] ), c[stk[tp--]] = cnt;
        tp--;
    }
}

//記憶化搜尋
//Memory search 
void DFS( int x ){
    v[x] = 1;
    for ( int i = hd[x]; i; i = nxt[i] )
        if ( !v[to[i]] ) DFS( to[i] );
}

int main(){
    scanf( "%d%d", &n, &p );
    for ( int i = 1; i <= n; ++i ) pr[i] = INT_MAX;
    for ( int i = 1; i <= p; ++i ) scanf( "%d%d", &x, &y ), pr[x] = y;
    scanf( "%d", &r );
    for ( int i = 1; i <= r; ++i ) scanf( "%d%d", &x, &y ), Add( x, y );
    
    // 先判斷是否能控制全部間諜,即把所有能收買的間諜都收買
    // To see whether all spies can be under control. 
    for ( int i = 1; i <= n; ++i ) if ( pr[i] < INT_MAX && !v[i] ) DFS( i );
    for ( int i = 1; i <= n; ++i ) if ( !v[i] ){ printf( "NO\n%d\n", i ); return 0; }
    //------------------判斷結束 Ended--------------------
    //因為已經確定所有間諜都能被控制,那就大膽地取來所有入度為0的節點(當然,是縮點後的圖)
    // Because all spies can be controled, get all ponits which has no indegree(After Tarjan, certainly).
    for ( int i = 1; i <= n; ++i ) if ( !c[i] ) Tarjan( i );
    //當然, 並不需要建邊,可以直接判斷入度是否為0
    //Certainly, it is unnecessary to connect edges.You can DIRECTLY know whether a point has no indegree.
    for ( int i = 1; i <= n; ++i )
        for ( int j = hd[i]; j; j = nxt[j] )
            if ( c[i] != c[to[j]] ) vs[c[to[j]]] = 1;
    int ans(0); 
    for ( int i = 1; i <= cnt; ++i ) if ( !vs[i] ) ans += f[i];
    printf( "YES\n%d\n", ans );
    return 0;
}

注:為了防止中文亂碼情況,程式碼註釋給出英文版。