思路:利用tarjan算法,记录每个点所属的连通分支,然后计算每个SCC的入度,最后计算每个SCC(入度为0)中花费最小的值
//强连通+缩点
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<string.h>
#define max 1111
using namespace std;
int N,M;
int X,Y,SCC;
int tem;
int n;
vector<int>gra[max];
stack<int>sta;
int num[max],vis[max];
int dfn[max],low[max];
int cost[max];
int belong[max];
int ind[max];
int MIN(int a,int b)
{
return a>b?b:a;
}
void tarjan(int x,int p)
{
vis[x]=2;
dfn[x]=low[x]=++n;
sta.push(x);
for(int i=0; i<gra[x].size(); i++)
{
int t=gra[x][i];
if(!dfn[t])
{
tarjan(t,x);
low[x]=MIN(low[x],low[t]);
}
else if(vis[t]==2)
{
low[x]=MIN(low[x],dfn[t]);
}
}
int min=99999999;
if(low[x]==dfn[x])
{
tem++;
while(!sta.empty())
{
int m=sta.top();
sta.pop();
vis[m]=1;
belong[m]=tem;//属于一个连通分支。
ind[m]=0;//入度为0,刚开始设置为0
if(x==m)
break;
}
}
}
int main()
{
int a,b;
while(scanf("%d%d",&N,&M)!=EOF)
{
SCC=tem=X=Y=n=0;
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
for(int i=0; i<max; i++)
gra[i].clear();
while(!sta.empty())
sta.pop();
for(int i=1; i<=N; i++)
scanf("%d",&num[i]);
for(int i=0; i<M; i++)
{
scanf("%d%d",&a,&b);
gra[a].push_back(b);
}
for(int i=1; i<=N; i++)
if(!dfn[i])
tarjan(i,0);
for(int j=1;j<=N;j++)
for(int i=0;i<gra[j].size();i++)
{
int gh=gra[j][i];
if(belong[j]!=belong[gh])//判断是不是属于一个连通分量,如果不是那么ind[belong[gh]]的入度+1
ind[belong[gh]]++;//让第belong[gh]的入度加1;
}
for(int i=1;i<=tem;i++)//tem连通分支的个数
{
if(ind[i]==0)//连通分支的入度为0,即可找到其值
SCC++;
cost[i]=99999999;//将每个连通分量的值设为999999999
}
for(int i=1;i<=N;i++)//枚举所有点
{
if(ind[belong[i]]==0)//判断当前的点是不是在一个入度为0的联通分支中
cost[belong[i]]=min(cost[belong[i]],num[i]);//求话费最小的值
}
for(int i=1;i<=tem;i++)
if(cost[i]!=99999999)
Y+=cost[i];
printf("%d %d\n",SCC,Y);
}
return 0;
}
hdu1827强连通+缩点
最新推荐文章于 2021-04-20 16:08:52 发布