【codevs4093】 EZ的間諜網絡 強連通分量+tarjan縮點

題目描述

由於外國間諜的大量滲入,國家安全正處於高度的危機之中。如果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:
【樣例1】
3
2
1 10
2 100
2
1 3
2 3
【樣例2】
4
2
1 100
4 200
2
1 2
3 4

題解:很明顯是用tarjan縮點求強連通分量,不過在此基礎上維護三個數組,ans,ru,color。
ans:每個強連通分量裏收買花費最少的間諜的費用,若沒有能收買的就賦爲極大值。
ru:判斷有沒有兩個強連通分量是相連的,如:x->y,那麼y的入度加一,那麼只需要計算入度爲0的點就可以。
color:相當於記錄每個點存在於哪個強連通分量裏。
維護三個數組,最後統計入度爲0的所有強連通分量的最低間諜價格就行。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 10006
using namespace std;
struct node
{
    int from,to,next;
}e[N];
int low[N],dfn[N],insex,stack[N],tot;
bool instack[N];
int t,ans[N];
int e_num,head[N];
bool flag[N];
int r,n,p,num[N],color[N],ru[N];
int a[N],b[N],s[N];
void add(int from,int to)
{
    ++e_num;
    e[e_num].from=from;
    e[e_num].to=to;
    e[e_num].next=head[from];
    head[from]=e_num;
}
void tarjan(int u)
{
    int i;
    low[u]=dfn[u]=++insex;
    instack[u]=true;
    stack[++tot]=u;
    for (i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if (!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[v],low[u]);
        }
        else if (instack[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if (low[u]==dfn[u])
    {
        int tmp=0;
        ++t;
        while (tmp!=u)
        {
            tmp=stack[tot--];
            instack[tmp]=false;
            ans[t]=min(ans[t],s[tmp]);
            color[tmp]=t;
        }
    }
}
void slove()
{
    int i;
    memset(dfn,0,sizeof(dfn));
    for (i=1;i<=n;i++)
        if (!dfn[i])
            tarjan(i);
}
int main()
{
    int x,y,i,sum=0;
    scanf("%d",&n);
    scanf("%d",&p);
    memset(s,127/3,sizeof(s));
    for (i=1;i<=p;i++)
    {
        scanf("%d%d",&x,&y);
        s[x]=y;
    }
    scanf("%d",&r);
    for (i=1;i<=r;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        a[i]=x;b[i]=y; //x指向y 
    }
    memset(ans,127/3,sizeof(ans));
    int maxx=ans[0];
    slove();
    int tt=t;
    for (i=1;i<=r;i++)
    {
        if (color[a[i]]!=color[b[i]])
        {
            ru[color[b[i]]]++;
        }
    }
    bool pp;
    int k;
    for (i=1;i<=t;i++)
        if (ans[i]==maxx&&!ru[i]) {pp=1;k=i;break;}
    if (pp==1) 
    {
        for (i=1;i<=n;i++)
            if (color[i]==k)
                {printf("NO\n%d",i);return 0;}
    }
    for (i=1;i<=tt;i++)
        if (ru[i]==0&&ans[i]!=maxx)
            sum+=ans[i];
    printf("YES\n%d",sum);
 } 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章