地址:
点击打开链接
这个的题目是根据上一个的题目的代码(模板),删改了一下。应该很好理解。这次得正向考虑,上一个题目是逆向思考的。
也就是直接算入度为0。代码如下:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 5250
#define Max 0x7fffffff
vector<int >mp[maxn]; // 记录每个结点所连接的结点
int stack[maxn*1000]; //用于记录访问过的节点
int degree[maxn]; //反向建图后,巨鹿出度,为了找出度为0的节点
int vis[maxn]; //dfs用,同时tarjan也用,用来记录访问
int low[maxn]; //记录low,这个不明白的话,就得去看tarjan缩点
int dfn[maxn]; //同上
int color[maxn]; //染色,也就是同样的环内颜色是一致的。
int bills[maxn];
int needNum,needBill;
int temp[maxn];
int n,m,cnt,tt,sig,leijia;
//主要用于缩点,说染色可能更加形象一些。
void Tarjan(int u)
{
vis[u]=1; //将该点设置为已经访问
low[u]=dfn[u]=cnt++; //初始化low和dfn , cnt就是每当一个点是新的,那么就cnt++,累加赋值
stack[++tt]=u; //将该点压栈,用数组进行模拟
//查看该点所连接的所有点
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i]; //找到该点
if(vis[v]==0)Tarjan(v); //如果该点没有被访问过,那么就访问该点
if(vis[v]==1)low[u]=min(low[u],low[v]); //如果访问过,那么就更新u点的值,这个地方也是tarjan缩点的规则
}
if(low[u]==dfn[u]) //如果构成了一个连通分量
{
sig++; //给这个分量分配一个索引
do
{
vis[stack[tt]]=-1; //将该点的访问归位,这个的作用???
color[stack[tt]]=sig;
}
while(stack[tt--]!=u); //将这个分量内所有的点全都赋值为该索引,如果用染色,那么就是把这个连通分量都染成一样的色
}
}
void Slove()
{
tt=-1;sig=0;cnt=1; //重置染色
for(int i=1;i<=n;i++)
{
if(vis[i]==0)Tarjan(i);
}//Tarjan染色
for(int i=1;i<=n;i++)
{
for(int j=0;j<mp[i].size();j++)
{
int v=mp[i][j]; //找到该点
if(color[i]!=color[v])
{
degree[color[v]]++; //设置出度
}
}
}//缩点重新建边,并且去重边。这里注意一下判重数组map【】【】我开的类型是bool的,开int会超内存,很蛋疼
memset(vis,0,sizeof(vis));
for(int i = 0 ; i <maxn ; i ++)
temp[i]=Max;
for(int i = 1 ; i<= n ; i ++)
{
if(degree[color[i]]==0)
{
if(!vis[color[i]])
{
needNum ++ ;
vis[color[i]] = 1;
}
if(temp[color[i]]>bills[i])
{
temp[color[i]]=bills[i];
}
}
}
for(int i = 1 ; i <= sig ; i ++)
{
if(temp[i]!=Max)
{
needBill += temp[i];
}
}
printf("%d %d\n",needNum,needBill);
}
void init()
{
memset(degree,0,sizeof(degree));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(color,0,sizeof(color));
memset(bills,0,sizeof(bills));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
needBill=needNum=0;
init();
for(int i = 1 ; i <= n ; i ++)
{
scanf("%d",&bills[i]);
}
for(int i=1;i<=n;i++)mp[i].clear();
//把每个点连接着谁都赋值
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
mp[x].push_back(y);
}
Slove();
}
}