洛谷 [P2661] 信息传递

来源:互联网 发布:游族网络天使纪元 编辑:程序博客网 时间:2024/05/29 08:06

求有向图的权值为一的最小环

并查集做法

维护一个dis[],表示i号元素到fa[i]的距离。
对于输入的每两个点u,v,询问这两个点的fa[]是否相同,如果相同就成环,维护最小值,mi=min(mi,dis[u]+dis[v]+1)。如果不相同,merge(u,v)。目测是最简单的做法。
但是只适用于权值为一的边。
注意对于一条U指向v的边,是将u合并到v上。

#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <algorithm>using namespace std;const int MAXN=200005;int read(){    int rv=0,fh=1;    char c=getchar();    while(c<'0'||c>'9'){        if(c=='-') fh=-1;        c=getchar();    }    while(c>='0'&&c<='9'){        rv=(rv<<1)+(rv<<3)+c-'0';        c=getchar();    }    return rv*fh;}int fa[MAXN],n,mi=0x7fffffff/3,dis[MAXN];int find(int x){    if(x!=fa[x]) {        int t=fa[x];fa[x]=find(fa[x]);dis[x]+=dis[t];    }    return fa[x];}void merge(int x,int y){    int r1=find(x),r2=find(y);    if(r1!=r2){        fa[r1]=r2;        dis[x]=dis[y]+1;    }}int main(){    freopen("in.txt","r",stdin);    n=read();    for(int i=1;i<=n;i++){        fa[i]=i;    }    for(int i=1;i<=n;i++){        int t=read();        if(find(i)==find(t)){            mi=min(mi,dis[i]+dis[t]+1);        }else {            merge(i,t);        }    }    cout<<mi;    fclose(stdin);    return 0;}

topsort

使用拓扑排序删掉那些一定不属于环上的点,再用dfs更新最小环的数量。

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <cmath>#include <queue>using namespace std;const int MAXN=200005;int read(){    int rv=0,fh=1;    char c=getchar();    while(c<'0'||c>'9'){        if(c=='-') fh=-1;        c=getchar();    }    while(c>='0'&&c<='9'){        rv=(rv<<1)+(rv<<3)+c-'0';        c=getchar();    }    return rv*fh;}struct edge{    int to,nxt;}e[MAXN];int n,head[MAXN],nume,in[MAXN],f[MAXN],mi=0x7fffffff/3,cnt;void adde(int from,int to){    e[++nume].to=to;    e[nume].nxt=head[from];    head[from]=nume;}queue<int> q;void dfs(int u){    cnt++;    f[u]=1;    for(int i=head[u];i;i=e[i].nxt){        int v=e[i].to;        if(!f[v]) dfs(v);    }}int main(){    freopen("in.txt","r",stdin);    n=read();    for(int i=1;i<=n;i++){        int t=read();        adde(i,t);        in[t]++;    }    for(int i=1;i<=n;i++){        if(!in[i]){            q.push(i),f[i]=1;        }    }    while(!q.empty()){        int u=q.front();q.pop();        for(int i=head[u];i;i=e[i].nxt){            int v=e[i].to;            in[v]--;            if(!in[v]&&!f[v]){                q.push(v);f[v]=1;            }        }    }    for(int i=1;i<=n;i++){        cnt=0;        if(!f[i]) {dfs(i);mi=min(mi,cnt);}    }    cout<<mi;    fclose(stdin);    return 0;}

tarjan求强连通分量

最小环即强连通分量

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <cmath>#include <queue>#include <stack>using namespace std;const int MAXN=200005;int read(){    int rv=0,fh=1;    char c=getchar();    while(c<'0'||c>'9'){        if(c=='-') fh=-1;        c=getchar();    }    while(c>='0'&&c<='9'){        rv=(rv<<1)+(rv<<3)+c-'0';        c=getchar();    }    return rv*fh;}struct edge{    int to,nxt;}e[MAXN];int n,head[MAXN],nume,dfn[MAXN],low[MAXN],in,mi=0x7fffffff/3;stack <int>s;bool f[MAXN];void adde(int from,int to){    e[++nume].to=to;    e[nume].nxt=head[from];    head[from]=nume;}void tarjan(int u){    dfn[u]=++in;    low[u]=++in;    f[u]=1;    s.push(u);    for(int i=head[u];i;i=e[i].nxt){        int v=e[i].to;        if(!dfn[v]){            tarjan(v);            low[u]=min(low[u],low[v]);        }else if(f[v]){            low[u]=min(low[u],dfn[v]);        }    }    if(dfn[u]==low[u]){        int v=0;        int cnt=0;        do{            v=s.top();            s.pop();            f[v]=0;            cnt++;        }while(v!=u);        mi=min(mi,cnt);    //  cout<<u<<" "<<cnt<<endl;    }}int main(){    freopen("in.txt","r",stdin);    n=read();    for(int i=1;i<=n;i++){        int t=read();        adde(i,t);    }    for(int i=1;i<=n;i++){        if(!dfn[i]) tarjan(i);    }    cout<<mi;    fclose(stdin);    return 0;}
原创粉丝点击