根据题目要求,只需求出各个强连通分量,然后进行缩点。缩点后找出入度为0的点即是需要打的电话数,也就是说找出能覆盖整张关系网的点。因为要求费用最低,所以在求出一个强连通分量时(栈弹出时)找到最小值,记录起来代表该连通分量(即缩点)的值。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#define SIZE 1001
using namespace std;
struct node
{
int to,next;
}edge[SIZE<<4];
int head[SIZE],idx;
int N,M,val[SIZE],ans;
int dfsn[SIZE],low[SIZE],time;
bool vis[SIZE];
int belong[SIZE],num,mincost[SIZE],deg[SIZE];
stack <int> sta;
void addnode(int from,int to)
{
edge[idx].to = to;
edge[idx].next = head[from];
head[from] = idx++;
}
void tarjan(int root)
{
dfsn[root] = low[root] = ++time;
sta.push(root);
vis[root] = true;
for(int i=head[root]; i!=-1; i=edge[i].next)
{
int to = edge[i].to;
if(!dfsn[to])
{
tarjan(to);
if(low[to] < low[root])
low[root] = low[to];
}
else if(vis[to] && dfsn[to] < low[root])
low[root] = dfsn[to];
}
int t = 0,tem = 0xfffffff;
if(dfsn[root] == low[root])
{
num ++;
while(t != root)
{
t = sta.top();
sta.pop();
vis[t] = false;
belong[t] = num; //同一个强连通分量,缩点
tem = min(tem,val[t]); //求最小值
}
mincost[num] = tem; //将最小值作为该连通分量的代表
}
}
void solve()
{
memset(dfsn,0,sizeof(dfsn));
memset(vis,0,sizeof(vis));
memset(deg,0,sizeof(deg));
ans = time = num = 0;
for(int i=1; i<=N; i++)
if(!dfsn[i])
tarjan(i);
for(int i=1; i<=N; i++)
for(int j=head[i]; j!=-1; j=edge[j].next)
if(belong[i] != belong[edge[j].to])
deg[belong[edge[j].to]] ++;
int sum = 0;
for(int i=1; i<=num; i++)
if(!deg[i])
{
ans += mincost[i];
sum ++;
}
printf("%d %d\n",sum,ans);
}
int main()
{
while(~scanf("%d%d",&N,&M))
{
for(int i=1; i<=N; i++)
scanf("%d",&val[i]);
int s,e;
idx = 0;
memset(head,-1,sizeof(head));
for(int i=1; i<=M; i++)
{
scanf("%d%d",&s,&e);
addnode(s,e);
}
solve();
}
return 0;
}