P1262 间谍网络-topo-tarjan

  • P1262 间谍网络
  • 题意:
  • 由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
  • 我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每个间谍分别用1到3000的整数来标识。
  • 请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。
  • 思路:缩点跑一下,这个连通块里面的只要一个能收买就都可以被逮捕,所以求一下缩点后的图 每个超级点需要收买的prize
  • 缩完点后也不需要全都收买,只要入度为0的点都能够被收买就可以了,即是解也是最优解,topo检查一遍即可
  • #include<bits/stdc++.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define maxn 8888
    vector<int>g[maxn];
    int w[maxn],n,p,r,a,b,color[maxn];
    int low[maxn],dfn[maxn],pri[maxn];
    int stk[maxn],tot,top,id,in[maxn];
    bool instk[maxn];
    void tarjan(int u)
    {
        int v;
        low[u]=dfn[u]=++tot;
        stk[++top]=u;
        instk[u]=1;
        for(int j=0; j<g[u].size(); j++)
        {
            v=g[u][j];
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[v],low[u]);
            }
            else if(instk[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u])
        {
            ++id;
            do
            {
                v=stk[top--];
                color[v]=id;
                instk[v]=0;
            }
            while(v!=u);
        }
    }
    void topo()
    {
        int ans=0;
        for(int i=1; i<=id; i++)
            if(in[i]==0)
            {
                if(pri[i]==inf)
                {
                    printf("NO\n");
                    for(int q=1; q<=n; q++)
                        if(color[q]==i)
                        {
                            printf("%d\n",q);
                            return ;
                        }
                }
                ans+=pri[i];
            }
        printf("YES\n%d\n",ans);
    }
    int main()
    {
        memset(w,inf,sizeof(w));
        memset(pri,inf,sizeof(pri));
        scanf("%d%d",&n,&p);
        for(int i=0; i<p; i++)
        {
            scanf("%d%d",&a,&b);
            w[a]=b;
        }
        scanf("%d",&r);
        while(r--)
        {
            scanf("%d%d",&a,&b);
            g[a].push_back(b);
        }
        for(int i=1; i<=n; i++)
            if(!dfn[i])tarjan(i);
        for(int i=1; i<=n; i++)
            if(w[i]!=inf)pri[color[i]]=min(pri[color[i]],w[i]);
        for(int i=1; i<=n; i++)
            for(int j=0; j<g[i].size(); j++)
                if(color[i]!=color[g[i][j]])
                    in[color[g[i][j]]]++;
        topo();
        return 0;
    }
    

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值