#include<bits/stdc++.h>
using namespace std;
int n,p,money[3005],m,dfn[3005],low[3005],num,cnt,mark[3005],minm[3005],inpoint[3005],ans;
bool buy[3005],vis[3005],flag[3005][3005];
const int MAXM=100000;
vector <int> G[3005];
stack <int> s;
void input() {
cin>>n>>p;
int idx;
for(int i=0;i<p;i++) {
cin>>idx;
cin>>money[idx];
buy[idx]=1;
}
cin>>m;
int a,b;
for(int i=0;i<m;i++) {
cin>>a>>b;
G[a].push_back(b);
}
}
void Tarjan(int i) {
dfn[i]=low[i]=++num;
s.push(i);
for(int j=0;j<G[i].size();j++) {
if(!dfn[G[i][j]]) {
Tarjan(G[i][j]);
low[i]=min(low[i],low[G[i][j]]);
}
else if(!mark[G[i][j]])
low[i]=min(low[i],dfn[G[i][j]]);
}
if(low[i]==dfn[i]) {
cnt++;
while(s.top()!=i) {
mark[s.top()]=cnt;
if(money[s.top()]<minm[cnt]) minm[cnt]=money[s.top()];
s.pop();
}
mark[s.top()]=cnt;
if(money[s.top()]<minm[cnt]) minm[cnt]=money[s.top()];
s.pop();
}
}
int main()
{
for(int i=0;i<3005;i++) {
minm[i]=money[i]=MAXM;
}
input();
for(int i=1;i<=n;i++)
if(!dfn[i] && buy[i]) Tarjan(i);
for(int i=1;i<=n;i++)
if(!dfn[i]) {
cout<<"NO\n"<<i<<endl;
return 0;
}
for(int i=1;i<=n;i++) {
if(!buy[i]) continue;
for(int j=0;j<G[i].size();j++) {
if(mark[i]!=mark[G[i][j]] && !flag[mark[i]][mark[G[i][j]]]) {
inpoint[mark[G[i][j]]]++;
flag[mark[i]][mark[G[i][j]]]=1;
}
}
}
for(int i=1;i<=cnt;i++) {
if(inpoint[i]==0 && minm[i]!=MAXM) ans+=minm[i];
}
cout<<"YES\n"<<ans<<endl;
return 0;
}
其实我就想的是先用Tarjan算法求SCC,同时记录每个点的SCC号码(就那个mark数组),以及每个SCC的最小花费(minm数组)。
然后main函数里面,输入+Tarjan后,先判断有没有没遍历过的点,有就说明不能控制所有间谍。随后再缩点(如果我这个算缩点的话),就把每个SCC当成一个整体,检索边,相应的SCC的入度增加1。最后再遍历每一个“SCC点”,入度为1的将其minm加到ans里。