洛谷 P2002 消息扩散

本文解析了一道关于图论的竞赛题目,通过缩点建新图的方法,利用DFS算法求解最小发布点数量,最终简化为统计新图中入度为零的节点数目。

题目背景
本场比赛第一题,给个简单的吧,这 100 分先拿着。
题目描述
有n个城市,中间有单向道路连接,消息会沿着道路扩散,现在给出n个城市及其之间的道路,问至少需要在几个城市发布消息才能让这所有n个城市都得到消息。
输入输出格式
输入格式:
第一行两个整数n,m表示n个城市,m条单向道路。
以下m行,每行两个整数b,e表示有一条从b到e的道路,道路可以重复或存在自环。
输出格式:
一行一个整数,表示至少要在几个城市中发布消息。
输入输出样例
输入样例#1:
5 4
1 2
2 1
2 3
5 1
输出样例#1:
2
说明
【数据范围】
对于20%的数据,n≤200;
对于40%的数据,n≤2,000;
对于100%的数据,n≤100,000,m≤500,000.
【限制】
时间限制:1s,内存限制:256M
【注释】
样例中在4,5号城市中发布消息。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<stack>
using namespace std;
#define MAXN 100005
#define MAXM 500005
struct Edge{ int to,next; }e[MAXM],ee[MAXM/10];
int head[MAXN],belong[MAXN],dfn[MAXN],low[MAXN];
stack<int> st;
int n,m,tot,cur,visx,sum_edge;
int id[MAXN],lik[MAXN],input[MAXN];
bool exist[MAXN],vis[MAXN];

inline void Add_Edge(int u,int v){ e[++tot].to=v,e[tot].next=head[u],head[u]=tot; }

void Tarjan(int u){
    dfn[u]=low[u]= ++visx;
    st.push(u); exist[u]=true;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(exist[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        int x=-1;  ++cur;
        do{
            x=st.top(),exist[x]=false;
            st.pop(),belong[x]=cur;
        }while(x!=u);
    }
}

inline void read(int &x){
    register char  c=getchar(); int f=1; x=0;
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

void Add_Edge_New(int u,int v){
    ee[++sum_edge].to=v,ee[sum_edge].next=lik[u],lik[u]=sum_edge;
}

inline bool cmp(int a,int b){ return input[a] < input[b]; }

void DFS(int u){
    if(vis[u]) return;
    vis[u]=true;
    for(int i=lik[u];i;i=ee[i].next){
        int v=ee[i].to;
        if(!vis[v]) DFS(v);
    }
}

int Main(){
    read(n),read(m);
    for(int u,v,i=1;i<=m;++i) read(u),read(v),Add_Edge(u,v);

    for(int i=1;i<=n;++i) if(!dfn[i]) Tarjan(i);

    for(int i=1;i<=n;++i)
        for(int j=head[i];j;j=e[j].next){
            int v=e[j].to;
            if(belong[v]!=belong[i]){
                Add_Edge_New(belong[i],belong[v]);
                ++input[belong[v]];
            }
        }
    int Ans=0;
    /*for(int i=1;i<=cur;++i) id[i]=i;
    sort(id+1,id+cur+1,cmp);

    for(int i=1;i<=cur;++i)
        if(!vis[id[i]]){
            ++Ans;
            DFS(id[i]);
        }*/
    for(int i=1;i<=cur;++i) if(!input[i]) ++Ans;
    printf("%d\n",Ans);

    return 0;
}
int Aptal_is_My_Son=Main();
int main(int argc,char *argv[]){ ; }

刚开始我想缩点,然后建新图,从入度为0的点开始DFS染色,调试之后A掉,其实并不需要染色,只需要统计新图上入度为零的点的个数就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值