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