題目描述
由於外國間諜的大量滲入,國家安全正處於高度的危機之中。如果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
解法:tarjan縮點
-
對於任何一個環,我們都可以去中間的最小值,因爲只要收買一個,剩下的就都可以控制
-
我們建完圖之後直接縮點即可,如何判斷不可以呢,如果一個點的dfn追溯值爲0,那我們直接輸出該點即可
-
對於任何一個人,我們枚舉和他相連的所有邊,如果中間的編號不一樣,即不是在一個環上,那我們就可以就可以把連接的那個人的入度加一,在後面累加答案時,我們只需要加上所有入度爲0的點即可,因爲沒有點可以到他,只能收買
AC代碼
#include<cstdio>
#include<stack>
#include<cstring>
#define inf 0x3f3f3f3f
#define re register int
using namespace std;
struct edge {
int nex,to;
}e[8080];
int n,p,r,ans,cnt,m[3030],head[3030],sum[3030];
int num,tot,dfn[3030],low[3030],a[3030],ru[3030];
bool v[3030]; stack<int> s;
inline int read() {
int x=0,cf=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-') cf=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*cf;
}
inline void add(int x,int y) {
e[++cnt].to=y,e[cnt].nex=head[x],head[x]=cnt;
}
inline int min(int A,int B) { return A<B?A:B; }
inline void tarjan(int x) {
dfn[x]=low[x]=++num;
s.push(x),v[x]=true;
for(re i=head[x];i;i=e[i].nex) {
int y=e[i].to;
if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
else if(v[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]) {
tot++;
while(x!=s.top()) {
a[s.top()]=tot;
sum[tot]=min(sum[tot],m[s.top()]);
v[s.top()]=false; s.pop();
}
a[s.top()]=tot;
sum[tot]=min(sum[tot],m[s.top()]);
v[x]=false; s.pop();
}
return;
}
int main() {
n=read(),p=read();
memset(m,0x3f,sizeof(m));
memset(sum,0x3f,sizeof(sum));
for(re i=1;i<=p;i++) {
int x=read();
m[x]=read();
}
r=read();
for(re i=1;i<=r;i++) {
int x=read(),y=read();
add(x,y);
}
for(re i=1;i<=n;i++) {
if(!dfn[i]&&m[i]!=inf) tarjan(i);
}
for(re i=1;i<=n;i++) {
if(!dfn[i]) {
printf("NO\n%d",i);
return 0;
}
}
for(re i=1;i<=n;i++) {
for(re j=head[i];j;j=e[j].nex) {
if(a[i]!=a[e[j].to]) ru[a[e[j].to]]++;
}
}
for(re i=1;i<=tot;i++) {
if(!ru[i]) ans+=sum[i];
}
printf("YES\n%d",ans);
return 0;
}