天天写算法之(Tarjan缩点)Summer Holiday

地址: 点击打开链接
这个的题目是根据上一个的题目的代码(模板),删改了一下。应该很好理解。这次得正向考虑,上一个题目是逆向思考的。

也就是直接算入度为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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值