• NOIP2018 复赛 图论模板集锦(精)



    [图论模板]

    最短路径问题(无负边权)

      Time Limit: 1 Sec
      Memory Limit: 128 MB

    Description###

       给定有向图 G,以及原点 S,请求出原点到所有点的最短路径。
     

    Input###

       输入文件的第一行包含两个整数 n, m,代表图中的顶点数和边数。
    接下来 m 行,每行三个整数 u, v, w,代表一条从 u 指向 v,权值 为 w 的边。
    最后一行为一个整数 S。
     

    Output###

       输出 n 个整数,依次代表 S 到 1, 2, . . . , n 的最短距离, S 到自己的距 离定义为 0,对于无法从 S 到达的点输出 -1。整数用一个空格隔开。
     

    Sample Input###

      3 3
      1 2 1
      2 3 2
      1 3 1
      1
     

    Sample Output###

      0 1 1
      

    HINT

      对于100%的数据,n ≤ 100000,m ≤ 800000,0 ≤ w ≤ 1000,请使用 dijkstra 算法。


    AC代码

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define pa pair<int,int>
    using namespace std;
    const int N=1e5+5,inf=1e9;
    int n,m;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    struct edge{
        int to,val,next;
    }e[N<<3];
    int cnt_edge,last[N];
    inline void add_edge(int u,int v,int w){
        e[++cnt_edge]=(edge){v,w,last[u]};last[u]=cnt_edge;
    }
    int dis[N];bool vis[N];
    priority_queue<pa,vector<pa>,greater<pa> >q;
    void Dijkstra(){
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)dis[i]=inf;
        dis[1]=0;q.push(make_pair(0,1));
        while(!q.empty()){
            int u=q.top().second;q.pop();
            vis[u]=1;
            for(int i=last[u];i;i=e[i].next){
                int v=e[i].to;
                if(vis[v])continue;
                if(dis[v]>dis[u]+e[i].val){
                    dis[v]=dis[u]+e[i].val;
                    q.push(make_pair(dis[v],v));
                }
            }
        }
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            add_edge(u,v,w);
        }
        Dijkstra();
        for(int i=1;i<n;i++)
            if(dis[i]==inf)printf("-1 ");
            else printf("%d ",dis[i]);
        if(dis[n]==inf)printf("-1
    ");
        else printf("%d
    ",dis[n]);
        return 0;
    }
    

    最短路径问题(二)(有负边权)

      Time Limit: 1 Sec
      Memory Limit: 128 MB

    Description###

       给定有向图 G,以及原点 S,请求出原点到所有点的最短路径。
     

    Input###

       输入文件的第一行包含两个整数 n, m,代表图中的顶点数和边数。
    接下来 m 行,每行三个整数 u, v, w,代表一条从 u 指向 v,权值 为 w 的边。
    最后一行为一个整数 S。
     

    Output###

      若图中存在负权环,则输出一行“ERROR”。
    否则输出 n 个整数,依次代表 S 到 1, 2, . . . , n 的最短距离, S 到自己 的距离定义为 0,对于无法从 S 到达的点输出 -1。整数用一个空格隔开。
     

    Sample Input###

    Sample Output###

    HINT

      对于所有数据, n ≤ 1000, m ≤ 100000, −1000 ≤ w ≤ 1000。


    AC代码

    #include <cstdio>
    #include <cstdlib>
    #include <queue>
    using namespace std;
    const int N=1e3+5,inf=1e9;
    int n,m;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct edge{
        int to,val,next;
    }e[100005];
    int cnt_edge,last[N];
    void add_edge(int u,int v,int w){
        e[++cnt_edge]=(edge){v,w,last[u]};last[u]=cnt_edge;
    }
    queue<int> q;
    int dis[N],ins[N];
    bool inq[N];
    void SPFA(){
        for(int i=1;i<=n;i++)dis[i]=inf;
        dis[1]=0;
        q.push(1);
        inq[1]=1;ins[1]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            inq[u]=0;
            for(int i=last[u];i;i=e[i].next){
                int v=e[i].to;
                if(dis[v]>dis[u]+e[i].val){
                    dis[v]=dis[u]+e[i].val;
                    if(!inq[v]){
                        q.push(v);
                        inq[v]=1;
                    }
                    ins[v]++;
                    if(ins[v]==n){
                        puts("ERROR");
                        exit(0);
                    }
                }
            }
        }
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            add_edge(u,v,w);
        }
        SPFA();
        for(int i=1;i<n;i++)
            if(dis[i]==inf)printf("-1 ");
            else printf("%d ",dis[i]);
        if(dis[n]==inf)printf("-1
    ");
        else printf("%d
    ",dis[n]);
        return 0;
    }
    

    最小圈

      Time Limit: 1 Sec
      Memory Limit: 128 MB

    Description###

       给定一个带权无向图 G = (V, E),请求出其中的最小圈。圈定义为 顶点序列 (v1,v2,...,vp,vp+1),满足 vi = vj(i = j), v1 = vp+1,且边 (vi, vi+1) ∈ E。
     

    Input###

       第一行为两个整数 n,m,代表图的顶点数和边数。
       接下来 m 行,每行三个整数 u,v,w,描述一条连接 u 和 v,权值为 w 的边。
     

    Output###

       输出一个整数,即图中的最小圈。数据保证图中至少存在一个圈。
     

    Sample Input###

      6 7
      1 2 1
      2 4 2
      4 6 3
      5 6 4
      3 5 5
      3 4 2
      1 3 6
     

    Sample Output###

      11
      

    HINT

      对于 100% 的数据, n ≤ 400, w ≤ 100000
      利用 Floyd 算法,求得环中的点最大编号为 1, 2, . . . , n 时的最小环。


    AC代码

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int inf=1e8;
    int n,m,mn;
    int mp[405][405],dis[405][405];
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                dis[i][j]=dis[j][i]=mp[i][j]=mp[j][i]=inf;
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            dis[u][v]=dis[v][u]=mp[u][v]=mp[v][u]=min(dis[u][v],w);
        }
        mn=inf;
        for(int k=1;k<=n;k++){
            for(int i=1;i<k;i++)
                for(int j=i+1;j<k;j++)
                    mn=min(mn,dis[i][j]+mp[i][k]+mp[k][j]);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        }
        printf("%d
    ",mn);
        return 0;
    }
    

    公路修建(有坑 有负边权 的最大生成树)

      Time Limit: 1 Sec
      Memory Limit: 128 MB

    Description###

       有 n 个城市需要用道路连接起来,现在有 m 条道路的修建方案,每 个方案的实施可以带来一定的利润,你的任务是确定实施的方案,使得任 意两个城市都连通,并且获得的利润最大。
     

    Input###

       第一行为两个整数 n, m,含义如题中所述。n ≤ 10000, m ≤ 200000。
    接下来有 m 行,每行三个整数 u, v, w,代表在 u 和 v 之间修建一 条道路可以获得 w 的利润。注意,修建一条道路可能会导致亏损,这时我 们用负的利润表示。
     

    Output###

       输出一行,代表能够获得的最大利益。
     

    Sample Input###

      3 3
      1 2 -1
      2 3 -2
      1 3 -1
     

    Sample Output###

      -2
      

    HINT


    AC代码

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n,m,cnt,ans,fa[10005];
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct edge{
        int u,v,w;
    }e[200005];
    bool cmp(edge a,edge b){
        return a.w>b.w;
    }
    int find(int x){
        while(x!=fa[x])x=fa[x]=fa[fa[x]];
        return x;
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            fa[i]=i;
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            if(w>=0){
                ans+=w;
                int fx=find(u);
                int fy=find(v);
                if(fx!=fy)fa[fx]=fy;
            }else e[++cnt]=(edge){u,v,w};
        }
        sort(e+1,e+cnt+1,cmp);
        for(int i=1;i<=cnt;i++){
            int fx=find(e[i].u);
            int fy=find(e[i].v);
            if(fx!=fy){
                ans+=e[i].w;
                fa[fx]=fy;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    1504: 刻录光盘 (求强连通分量)

      Time Limit: 1 Sec
      Memory Limit: 128 MB

    Description###

       在 JSOI2005 夏令营快要结束的时候,很多营员提出来要把整个夏令 营期间的资料刻录成一张光盘给大家,以便大家回去后继续学习。组委会 觉得这个主意不错!可是组委会一时没有足够的空光盘,没法保证每个人 都能拿到刻录上资料的光盘,又来不及去买了,怎么办呢?!
    组委会把这个难题交给了 LHC,LHC 分析了一下所有营员的地域关 系,发现有些营员是一个城市的,其实他们只需要一张就可以了,因为一 个人拿到光盘后,其他人可以带着 U 盘之类的东西去拷贝啊!
    可是,LHC 调查后发现,由于种种原因,有些营员并不是那么的合 作,他们愿意某一些人到他那儿拷贝资料,当然也可能不愿意让另外一些 人到他那儿拷贝资料,这与我们 JSOI 宣扬的团队合作精神格格不入!
    现在假设总共有 N 个营员(2<=N<=2000),每个营员的编号为 1 N。 LHC 给每个人发了一张调查表,让每个营员填上自己愿意让哪些人到他那 儿拷贝资料。当然,如果 A 愿意把资料拷贝给 B,而 B 又愿意把资料拷 贝给 C,则一旦 A 获得了资料,则 B,C 都会获得资料。
    现在,请你编写一个程序,根据回收上来的调查表,帮助 LHC 计算 出组委会至少要刻录多少张光盘,才能保证所有营员回去后都能得到夏令 营资料?
     

    Input###

      先是一个数 N,接下来的 N 行,分别表示各个营员愿意把自己获得的 资料拷贝给其他哪些营员。即输入数据的第 i+1 行表示第 i 个营员愿意把 资料拷贝给那些营员的编号,以一个 0 结束。如果一个营员不愿意拷贝资 料给任何人,则相应的行只有 1 个 0,一行中的若干数之间用一个空格隔 开。
     

    Output###

       一个正整数,表示最少要刻录的光盘数。
     

    Sample Input###

      5
      2 4 3 0
      4 5 0
      0
      0
      1 0
     

    Sample Output###

      1
      

    HINT


    AC代码

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N=2005;
    int n,m,num,ans;
    int U[N*N],V[N*N],in[N];
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct edge{
        int to,next;
    }e[N*N],_e[N*N];
    int cnt_edge,last[N];
    void add_edge(int u,int v){
        e[++cnt_edge]=(edge){v,last[u]};last[u]=cnt_edge;
    }
    int _cnt_edge,_last[N];
    void _add_edge(int u,int v){
        _e[++_cnt_edge]=(edge){v,_last[u]};_last[u]=_cnt_edge;
    }
    int Time,top,col_num,col[N],dfn[N],low[N],q[N];
    bool ins[N];
    void Tarjan(int u){
        dfn[u]=low[u]=++Time;
        q[++top]=u;ins[u]=1;
        for(int i=last[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(ins[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u]){
            ++col_num;
            while(q[top]!=u){
                col[q[top]]=col_num;
                ins[q[top]]=0;
                top--;
            }
            col[q[top]]=col_num;
            ins[q[top]]=0;
            top--;
        }
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++){
            int x=read();
            while(x!=0){
                ++num;
                U[num]=i;
                V[num]=x;
                add_edge(i,x);
                x=read();
            }
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])Tarjan(i);
        for(int i=1;i<=num;i++)
            if(col[U[i]]!=col[V[i]])
            in[col[V[i]]]++;
        for(int i=1;i<=col_num;i++)
            if(!in[i])ans++;
        printf("%d
    ",ans);
        return 0;
    }
    

    割点和桥

      Time Limit: 1 Sec
      Memory Limit: 128 MB

    Description###

       给定连通无向图 G,请求出 G 中的割点和桥的个数。
     

    Input###

       第一行两个整数 n,m,代表图的顶点数和边数。 接下来 m 行,每行两个整数 u, v,描述一条无向边。 N ≤ 50000, m ≤ 200000
     

    Output###

       输出两个整数,先输出割点的数量和再输出桥的数量,用一个空格隔 开。
     

    Sample Input###

      4 5
       1 2
       1 2
       2 3
       2 3
       3 4
     

    Sample Output###

      2 1
      

    HINT


    AC代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int N=5e4+5;
    int n,m;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct edge{
        int to,next;
    }e[400005];
    int cnt_edge=1,last[N];
    inline void add_edge(int u,int v){
        e[++cnt_edge]=(edge){v,last[u]};last[u]=cnt_edge;
    }
    struct _edge{
        int from,to;
    };
    int Time,dfn[N],low[N];
    void init(){
        Time=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
    }
    bool isCut_point[N];
    void Tarjan_cut_point(int u){
        dfn[u]=low[u]=++Time;
        int child=0;
        for(int i=last[u];i;i=e[i].next){
            int v=e[i].to;
            if(!dfn[v]){
                Tarjan_cut_point(v);
                low[u]=min(low[u],low[v]);
                child++;
                if(u!=1 && low[v]>=dfn[u])isCut_point[u]=1;
            }else
                low[u]=min(low[u],dfn[v]);
        }
        if(u==1 && child>1)isCut_point[u]=1;
    }
    vector<_edge> bridge;
    void Tarjan_bridge(int u,int from){
        dfn[u]=low[u]=++Time;
        for(int i=last[u];i;i=e[i].next){
            int v=e[i].to;
            if(i==(from^1))continue;
            if(!dfn[v]){
                Tarjan_bridge(v,i);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])bridge.push_back((_edge){u,v});
            }else
                low[u]=min(low[u],dfn[v]);
        }
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read();
            add_edge(u,v);
            add_edge(v,u);
        }
        init();
        Tarjan_cut_point(1);
        init();
        Tarjan_bridge(1,0);
        int Cut_point=0;
        for(int i=1;i<=n;i++)
            if(isCut_point[i])Cut_point++;
        printf("%d %d
    ",Cut_point,(int)bridge.size());
        return 0;
    }
    

      未完待续


      作者:skl_win
      出处:https://www.cnblogs.com/shaokele/
      本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    VS2013中使用码云gitee建立源代码管理
    win10激活出现错误0xc004f074 解决方案
    List<string> 去重复 并且出现次数最多的排前面
    jQuery.extend(),jQuery.fn.extend() 区别
    js 获取范围内的随机数
    xslt/xpath对不存在属性的判断问题
    查询各科成绩不合格与合格人数
    微信开发之实现一键拨号及短信发送功能
    Git 常用命令
    Excel 表 导入导出
  • 原文地址:https://www.cnblogs.com/shaokele/p/9844121.html
Copyright © 2020-2023  润新知