描述:(给你一个有向图,其中某些点是可以买的,如果你买了一个点,)
(你可以到达这个点指向的点,指向的这个点又能到它指向的点......(直到走不通),我们要选择一些点买,使图联通且代价最小!)
算法:tarjan缩点
思考过程
选择哪一些点,能使图联通且代价最小呢?
我们试着想一下,如果一个点被其他点指向,那我们最好选那个点
如果那个点也被其他点指向,我们一直追溯过去,入度为0的点必须要选了
但是如果存在环,那么没有点入度为0,这个时候就需要缩点,变成我们上面的那种情况
缩点后环的代价就是环中点的最小代价
问题又来了,如果环中没有点可以买怎么办??
所以tarjan的时候只对那些可以买的点进行找环
最后扫一遍,如果有点的dfn为0,说明没有遍历,即它是孤立的,没办法买到的,输出NO~
#include <bits/stdc++.h>
using namespace std;
const int maxn=16009;
const int inf=999999999;
int n,m,q;
struct p{
int to,nxt,w;
}d[maxn];int head[maxn],cnt=1;
void add(int u,int v,int w){
d[cnt].to=v,d[cnt].w=w,d[cnt].nxt=head[u],head[u]=cnt++;
}
int indug[maxn],monney[maxn],sumn[maxn];
int low[maxn],dfn[maxn],stac[maxn],vis[maxn],top,id,sd[maxn],fen;
void tarjan(int now)
{
dfn[now]=low[now]=++id;
stac[++top]=now,vis[now]=1;
for(int i=head[now];i;i=d[i].nxt)
{
int v=d[i].to;
if(!dfn[v]){
tarjan(v);
low[now]=min(low[now],low[v]);
}
else if(vis[v]) low[now]=min(low[now],low[v]);
}
if(dfn[now]==low[now])
{
fen++;int temp;
while(temp=stac[top--])
{
vis[temp]=0;
sd[temp]=fen;
sumn[fen]=min(sumn[fen],monney[temp]);
if(temp==now) break;
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) monney[i]=sumn[i]=inf;
for(int i=1;i<=m;i++)
{
int l,r;cin>>l>>r;
indug[l]++;monney[l]=r;
}
cin>>q;
for(int i=1;i<=q;i++)
{
int l,r;cin>>l>>r;
add(l,r,0);indug[r]++;
}
for(int i=1;i<=n;i++)
if(indug[i]==0)
{
cout<<"NO"<<endl<<i;
return 0;
}
for(int i=1;i<=n;i++)//开始缩点
if(!dfn[i]&&monney[i]!=-1) tarjan(i);
memset(indug,0,sizeof(indug));
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=d[j].nxt)
{
int v=d[j].to;
if(sd[i]==sd[v]) continue;
indug[sd[v]]++;
}
int ans=0;
for(int i=1;i<=fen;i++)
if(!indug[i]) ans+=sumn[i];
cout<<"YES"<<endl<<ans;
return 0;
}