题目链接:HDU-1827
主要思路:
先用Tarjian处理出强联通块,然后将每个点的边转为强联通块之间的边。然后连上一个个入度为0的强联通块中最小的结点即可。
正确性解释:用Tarjian算法处理出强连通块之后把每个强联通块看成是一个点,故这幅图肯定无环(若有环则早就并入一个强联通块内)。在将入度为0的点连通后就可以将所有入度不为0的点联通(每个点都有其最早的祖先,其入度一定为0)(可以自己多画几幅图理解一下)。
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 1005
using namespace std;
struct E {
int nx,to,from;
} edge[M<<1];//注意边的数量范围是2000
int tot=0,head[M];
void Addedge(int a,int b) {
edge[++tot].to=b;
edge[tot].from=a;
edge[tot].nx=head[a];
head[a]=tot;
}
int val[M];
int top,T,Bcnt;
int Belong[M],ID[M],low[M],stack[M],cost[M];
bool inst[M];
void Tarjian(int now) {
ID[now]=low[now]=++T;
stack[++top]=now;
inst[now]=1;
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
if(!ID[nxt]) {
Tarjian(nxt);
if(low[now]>low[nxt])low[now]=low[nxt];
} else {
if(inst[nxt]&&ID[nxt]<low[now])low[now]=ID[nxt];
}
}
int nxt;
if(ID[now]==low[now]) {
Bcnt++;
cost[Bcnt]=1e9;//初始要是很大的一个值
do {
nxt=stack[top--];
inst[nxt]=0;
Belong[nxt]=Bcnt;
cost[Bcnt]=min(cost[Bcnt],val[nxt]);//统计这个块内结点最小的费用
} while(now!=nxt);
}
}
int in[M];
void Init(int n){
tot=0;
for(int i=1;i<=n;i++)head[i]=0,ID[i]=0,in[i]=0;//初始化
}
void solve(int n,int m) {
int ans1=0,ans2=0;
top=T=Bcnt=0;
for(int i=1; i<=n; i++)if(!ID[i])Tarjian(i);//如果没有遍历到就遍历
for(int i=1;i<=m;i++){
if(Belong[edge[i].from]==Belong[edge[i].to])continue;//如果两个点在同一个强联通块内
in[Belong[edge[i].to]]++;//入度加1
}
for(int i=1;i<=Bcnt;i++){
if(!in[i])ans2+=cost[i],ans1++;//入度为0则连这个强联通块
}
printf("%d %d\n",ans1,ans2);
}
int main() {
int n,m;
while(~scanf("%d%d",&n,&m)) {
Init(n);//注意要初始化
for(int i=1; i<=n; i++)scanf("%d",&val[i]);
for(int i=1;i<=m;i++) {
int a,b;
scanf("%d%d",&a,&b);
Addedge(a,b);
}
solve(n,m);
}
}