间谍网络题解

题目传送门

题面

题目描述

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

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

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

输入格式

第一行只有一个整数 n n n

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

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

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

输出格式

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

输入输出样例 #1

输入 #1

3
2
1 10
2 100
2
1 3
2 3

输出 #1

YES
110

输入输出样例 #2

输入 #2

4
2
1 100
4 200
2
1 2
3 4

输出 #2

NO
3

思路

先遍历所有间谍。如果一个间谍可以被购买且未被访问过就用它做一次 Tarjan,控制每个强连通分量的成本为这个强连通分量价格最低的间谍。之后没访问过的间谍控制不了。如果可以控制所有间谍,容易知道应当购买入度为 0 0 0 的间谍。输出购买这些间谍的总花费即可。

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int n,r,p;
int buy[3003];
vector <int> pict[3003];
int dfn[3003],low[3003],nowdfn = 1,buyscc[3003];
bool instack[3003];
int scc[3003],scs,ans;
int insum[3003];
stack <int> nowblock;
void tarjan(int now)
{
    dfn[now] = low[now] = nowdfn++;
    nowblock.push(now);
    instack[now] = 1;
    for(auto i : pict[now])
    {
        if(!dfn[i])
        {
            tarjan(i);
            low[now] = min(low[now],low[i]);
        }
        else if(instack[i]) low[now] = min(low[now],dfn[i]);
    }
    if(dfn[now] == low[now])
    {
        scs++;
        buyscc[scs] = -1;
        while(nowblock.top() != now)
        {
            scc[nowblock.top()] = scs;
            if(buy[nowblock.top()] != -1)
            {
                if(buyscc[scs] == -1) buyscc[scs] = buy[nowblock.top()];
                else buyscc[scs] = min(buyscc[scs],buy[nowblock.top()]);
            }
            instack[nowblock.top()] = 0;
            nowblock.pop();
        }
        if(buy[nowblock.top()] != -1)
        {
            if(buyscc[scs] == -1) buyscc[scs] = buy[nowblock.top()];
            else buyscc[scs] = min(buyscc[scs],buy[nowblock.top()]);
        }
        scc[nowblock.top()] = scs;
        instack[nowblock.top()] = 0;
        nowblock.pop();
    }
}
int main()
{
    cin >> n >> p;
    for(int i = 1;i <= n;i++) buy[i] = -1;
    for(int i = 1;i <= p;i++)
    {
        int a,b;
        cin >> a >> b;
        buy[a] = b;
    }
    cin >> r;
    for(int i = 1;i <= r;i++)
    {
        int a,b;
        cin >> a >> b;
        pict[a].push_back(b);
    }
    for(int i = 1;i <= n;i++) if(!dfn[i] && buy[i] != -1) tarjan(i);
    for(int i = 1;i <= n;i++)
    {
        if(!dfn[i])
        {
            cout << "NO\n" << i;
            return 0;
        }
    }
    cout << "YES\n";
    for(int i = 1;i <= n;i++)
    {
        for(auto j : pict[i])
        {
            if(scc[i] != scc[j]) insum[scc[j]]++;
        }
    }
    for(int i = 1;i <= scs;i++)
    {
        if(!insum[i]) ans += buyscc[i];
    }
    cout << ans;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值