题意
Wiskey
W
i
s
k
e
y
有
n
n
个朋友,他想告诉 个朋友一个消息。他有所有朋友的联系方式,打给第
i
i
个朋友将花费 ,
n
n
个朋友内部有 对联系方式,每个联系方式(单向)表示
a
a
知道 的电话,消息可以通过朋友传递。求让这
n
n
个朋友都知道消息 至少要通知多少人,至少所需多少电话费。
1≤n≤1000
1
≤
n
≤
1000
1≤m≤2000
1
≤
m
≤
2000
思路
这道题发挥的才是强连通的作用——强连通缩点。
m
m
条关系链不一定有拓扑序,没有拓扑序就说明有环了,但是,如果筛出每一个强连通分量,不难发现,对于没一个强连通分量里的人,只要通知其中一人,剩下的人自然就会得知消息。所以,每一个强连通分量都可以缩成一个点,点权取电话费最小值,而连接不同节点的边,也可以视为连接不同强连通分量的边。
这种强连通缩点图最强大的地方在于,它从一个普通有向图变成了一张有向无环图
Acyclic
A
c
y
c
l
i
c
Graph)
G
r
a
p
h
)
。获得了拓扑序,使得
DP
D
P
和贪心都可以甩开“环”这个不小的障碍。对于这道题,只用在强连通缩点图中,通知入度为
0
0
<script type="math/tex" id="MathJax-Element-96">0</script> 的即可。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<stack>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 1003
#define M 2003
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v){to[++tot]=v,nxt[tot]=head[u];head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,M>G;
stack<int>stk;
int dfn[N],belong[N],low[N],cost[N],Cost[N],ind[N];
int ord,n,m,B,ans,cnt;
void tarjan(int u)
{
dfn[u]=low[u]=++ord;
stk.push(u);
EOR(i,G,u)
{
int v=G.to[i];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
if(!belong[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
int v;B++;
do
{
v=stk.top();stk.pop();
belong[v]=B;
}while(u!=v);
}
}
void clear()
{
B=ord=ans=cnt=0;
memset(belong,0,sizeof(belong));
memset(dfn,0,sizeof(dfn));
memset(Cost,0x7f,sizeof(Cost));
memset(ind,0,sizeof(ind));
G.clear();
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
clear();
FOR(i,1,n)scanf("%d",&cost[i]);
FOR(i,1,m)
{
int u,v;
scanf("%d%d",&u,&v);
G.add(u,v);
}
FOR(i,1,n)if(!dfn[i])tarjan(i);
FOR(u,1,n)
{
Cost[belong[u]]=min(Cost[belong[u]],cost[u]);
EOR(i,G,u)
{
int v=G.to[i];
if(belong[u]==belong[v])continue;
ind[belong[v]]++;
}
}
FOR(i,1,B)if(!ind[i])
{
ans+=Cost[i];
cnt++;
}
printf("%d %d\n",cnt,ans);
}
return 0;
}