题意
给定N个人,M条关系(N <=1000, M<= 2000),,如果a到b有边则表示第a个人能够联系到第b个人,联系第i个人的花费为v[i],联系具有传递性。现在需要联系到所有人,求最小联系人数和最小花费。
题解
考虑使用tarjan算法求解出所有强连通分量,然后强连通分量缩点(染色),然后考虑最小花费。自然想到如果一个部分(点)没有人指向它,那么一定要通知他,也就对应图中的入度。若一个连通分量入度为0,则一定要选中这个连通分量,并且自然选择这个连通分量中花费最小的。
可以在Tarjan的规程中,求出每个连通分量连通的最小花费。对缩点的图进行一次dfs,求出入度,最后统计答案即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 2005;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int N,M;
int cost[1005];
struct Tarjan{
int head[nmax],low[nmax],dfn[nmax],sstack[nmax],color[nmax],sz[nmax],top,sccnum,dfnnum,tot,n;
int cnt = 0; ll ans ;
int in[nmax],out[nmax],mincost[nmax];
bool visit[nmax];
struct edge{int to,nxt;}e[nmax<<1];
void init(int n){
memset(head,-1,sizeof head);
memset(mincost,INF,sizeof mincost);
memset(dfn,0,sizeof dfn);
memset(color,0,sizeof color);
memset(visit,0,sizeof visit);
memset(sz,0,sizeof sz);
memset(in,0,sizeof in);
memset(out,0,sizeof out);
ans = cnt = dfnnum = top = sccnum = tot = 0;
this->n = n;
}
void add_edge(int u,int v){
e[tot].to = v, e[tot].nxt = head[u]; head[u] = tot++;
}
void tarjan(int u){
low[u] = dfn[u] = ++dfnnum;
visit[u] = true;
sstack[++top] = u;
for(int i = head[u] ; i != -1; i = e[i].nxt){
int v = e[i].to;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}else if(visit[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]){
visit[u] = false;
color[u] = ++ sccnum;
// printf("scc %d %d",sccnum,u);
sz[sccnum]++;
mincost[sccnum] = min(mincost[sccnum],cost[u]);
while(sstack[top] != u){
// printf(" %d",sstack[top]);
color[sstack[top]] = sccnum;
sz[sccnum]++;
visit[sstack[top]] = false;
mincost[sccnum] = min(mincost[sccnum],cost[sstack[top]]);
top--;
}
// printf("\n");
top--;
}
}
void dfs(int u){
visit[u] = true;
for(int i = head[u];i!=-1;i=e[i].nxt){
int v = e[i].to;
if(!visit[v]){
if(color[v] != color[u]) in[color[v]]++,out[color[u]]++;
dfs(v);
}else if(visit[v]){
if(color[v] != color[u]) in[color[v]]++,out[color[u]]++;
}
}
}
void start(){for(int i = 1;i<=n;++i) if(!dfn[i]) tarjan(i);}
void get_ans(){
// if(sccnum == 1) return 0;
memset(visit,0,sizeof visit);
for(int i = 1;i<=n;++i) if(!visit[i]) dfs(i);
// ll = 0;
for(int i = 1;i<=sccnum;++i){
// printf("in %d %d\n",i,in[i]);
if(!in[i]) ans += (ll)mincost[i],cnt++;
// if(!out[i]) notout++;
}
// return ans;
}
}solver;
int main(){
while(scanf("%d %d",&N,&M)!=EOF){
solver.init(N); int u, v;
for(int i = 1;i<=N;++i) scanf("%d",&cost[i]);
for(int i = 1;i<=M;++i){
scanf("%d %d",&u,&v);
solver.add_edge(u,v);
}
solver.start();
// printf("%d\n",solver.sccnum);
solver.get_ans();
printf("%d %lld\n",solver.cnt,solver.ans);
}
return 0;
}