zoukankan      html  css  js  c++  java
  • c++算法模板(二)

    <6>最短路问题:

    1.      Flored-warshall 算法(n^3)

      for(int k=1;k<=n;k++)

              for(int i=1;i<=n;i++)

                for(int j=1;j<=n;j++)

              if((dis[i][j]>dis[i][k]+dis[k][j]) && (i!=k) && (i!=j) && (k!=j))

              {

                      dis[i][j]=dis[i][k]+dis[k][j];

                      pre[i][j]=pre[k][j];

              }

    输出路径函数:

    在调用处加上:cout<<s<<” ”;   //s为起点;

    void print (int x)

     {

             if(pre[s][x]==0) return ;

             print(pre[s][x]);

             cout<<"->"<<x;

     }

    2.      dijkstra算法(n^2)

    无优化,用了现写的快读搞定。洛谷的模板题P3371.cin流最后一个点超时,scanf和快读都是可以过的。当数据规模比较大的时候用scanf好咯。

    #include<iostream>

    #include<cstdio>

    #include<cstring>

    #include<cstdlib>

    #include<cmath>

    #include<algorithm>

    #define maxn 10010

    #define maxm 500010

    using namespace std;

    int num,minl,k,n,m,s;

    int v[maxn],dis[maxn],head[maxn];

    int read()

    {

    int out=0,fh=1;

    char cc=getchar();

    if (cc=='-')fh=-1;

    while(cc>'9'||cc<'0')cc=getchar();

    while(cc>='0'&&cc<='9'){out=out*10+cc-'0';cc=getchar();}

    return out*fh;}

    /*void write(int x)

    {

    if(x==0){

    putchar('0');

    return;}

    int num=0;char c[15];

    while(x)c[++num]=(x%10)+48,x/=10;

    while(num) putchar(c[num--]);

    putchar(' ');}  */

    struct Edge

    {

    int next;

    int to;

    int dis;

    }edge[maxm];

    void add_edge (int u,int v,int w)

    {

    num++;

    edge[num].next=head[u];

    edge[num].to=v;

    edge[num].dis=w;

    head[u]=num;

    }

    void dijstra (int v0)

    {

    for(int i=1;i<=n;i++)

     dis[i]=2147483647;

    dis[v0]=0;

    for(int i=1;i<=n;i++)

    {

               minl=2147483647;

               k=0;

               for(int j=1;j<=n;j++)

               {

                        if(v[j]==0 && minl>dis[j])

                        {

                                 minl=dis[j];

                                 k=j;

                        }

               }

               v[k]=1;

               for(int i=head[k];i!=0;i=edge[i].next)

               {

                        if(v[edge[i].to]==0 && minl+edge[i].dis<dis[edge[i].to])

                        dis[edge[i].to]=minl+edge[i].dis;

               }

     }

    }

    int main()

    {

    cin>>n>>m>>s;

    for(int i=1;i<=m;i++)

    {

               int u,v,w;

               u=read();

               v=read();

               w=read();

               add_edge(u,v,w);

    }

    dijstra(s);

    for(int i=1;i<=n;i++)

    cout<<dis[i]<<" ";

    return 0;

     }

    3.Dijkstra(堆优化):

    实测516ms; 堆优化是指在寻找最近点时,用堆log时间复杂度取点,priority_queue(/优先队列)实现;

    较朴素算法,利用了堆,能更快取得最近点;

    #include<cstdio>

    #include<iostream>

    #include<algorithm>

    #include<queue>

    using namespace std;

    const int INF=2147483647;

    const int maxn=10000+10;

    const int maxm=500000+10;

    int n,m,s;

    int fir[maxn],nxt[maxm],to[maxm],val[maxm],cnt;

    void add_edge(int u,int v,int w)

    {

        nxt[++cnt]=fir[u];fir[u]=cnt;to[cnt]=v;val[cnt]=w;

    }

    struct Node {

        int d,id;

        Node(){}

        Node(int d,int id):d(d),id(id){}

        bool operator < (const Node& rhs) const {

            return d>rhs.d;//重载<,方便堆

        }

    };

    int dis[maxn],vis[maxn];

    void Dijkstra(int s)

    {

        for(int i=1;i<=n;i++) dis[i]=INF; dis[s]=0;

        priority_queue<Node>Q;

        Q.push(Node(0,s));

        while(!Q.empty()) {

            Node u=Q.top(); Q.pop();

            if(vis[u.id]) continue;//若某个点已经被更新到最优,就不用再次更新其他点

            vis[u.id]=1;

            for(int e=fir[u.id];e;e=nxt[e]) {

                int v=to[e],w=val[e];

                if(u.d+w<dis[v]) {

                    dis[v]=u.d+w;

                    Q.push(Node(dis[v],v));

                }

            }

        }

    }

    int main()

    {

        scanf("%d%d%d",&n,&m,&s);

        for(int u,v,w,i=0;i<m;i++) {

            scanf("%d%d%d",&u,&v,&w);

            add_edge(u,v,w);

        }

        Dijkstra(s);

        for(int i=1;i<=n;i++) printf("%d ",dis[i]);

        return 0;

    }

    4.SPFA(无优化):

    766ms;

    耗时主要原因是可能某个能将更多点尽可能优化的,却放进了队尾;

    #include<cstdio>

    #include<iostream>

    #include<algorithm>

    #include<queue>

    using namespace std;

    const int INF=2147483647;

    const int maxn=10000+10;

    const int maxm=500000+10;

    int n,m,s;

    int fir[maxn],nxt[maxm],to[maxm],val[maxm],cnt;

    void add_edge(int u,int v,int w)

    {

        nxt[++cnt]=fir[u];fir[u]=cnt;to[cnt]=v;val[cnt]=w;

    }

    int dis[maxn],inq[maxn];

    void SPFA(int s)

    {

        for(int i=1;i<=n;i++) dis[i]=INF; dis[s]=0;

        queue<int>Q;Q.push(s);

        while(!Q.empty()) {

            int u=Q.front(); Q.pop();

            for(int e=fir[u];e;e=nxt[e]) {

                int v=to[e],w=val[e];

                if(dis[u]+w<dis[v]) {

                    dis[v]=dis[u]+w;

                    if(!inq[v]) Q.push(v);

                }

            }

        }

    }

    int main()

    {

        scanf("%d%d%d",&n,&m,&s);

        for(int u,v,w,i=0;i<m;i++) {

            scanf("%d%d%d",&u,&v,&w);

            add_edge(u,v,w);

        }

        SPFA(s);

        for(int i=1;i<=n;i++) printf("%d ",dis[i]);

        return 0;

    }

    5.SPFA(SLF优化):

    实测497ms; SLF优化是指,当前进队的dis值与队首的dis值比较,<=进队首,否则进队尾,deque(双向队列)实现;

    #include<cstdio>

    #include<iostream>

    #include<algorithm>

    #include<queue>

    using namespace std;

    const int INF=2147483647;

    const int maxn=10000+10;

    const int maxm=500000+10;

    int n,m,s;

    int fir[maxn],nxt[maxm],to[maxm],val[maxm],cnt;

    void add_edge(int u,int v,int w)

    {

        nxt[++cnt]=fir[u];fir[u]=cnt;to[cnt]=v;val[cnt]=w;

    }

    int dis[maxn],inq[maxn];

    void SPFA(int s)

    {

        for(int i=1;i<=n;i++) dis[i]=INF; dis[s]=0;

        deque<int>Q;

        Q.push_front(s);

        while(!Q.empty()) {

            int u=Q.front(); Q.pop_front(); inq[u]=0;

            for(int e=fir[u];e;e=nxt[e]) {

                int v=to[e],w=val[e];

                if(dis[u]+w<dis[v]) {

                    dis[v]=dis[u]+w;

                    if(!inq[v]) {

                        if(Q.empty()||dis[v]<=dis[Q.front()]) Q.push_front(v);

                        else Q.push_back(v);

                        inq[v]=1;

                    }

                }

            }

        }

    }

    int main()

    {

        scanf("%d%d%d",&n,&m,&s);

        for(int u,v,w,i=0;i<m;i++) {

            scanf("%d%d%d",&u,&v,&w);

            add_edge(u,v,w);

        }

        SPFA(s);

        for(int i=1;i<=n;i++) printf("%d ",dis[i]);

        return 0;

    }

    总结对于此题而言,时间效率:SPFA(ELF优化)>Dijkstra(堆优)>SPFA>Dijkstra;

    实际上SPFA的时间复杂度不够稳定,有些时候易被出题人卡常数,建议使用更稳定的Dijkstra;

     

     <7>最小环问题

    ans为最小环的最优值:

    for(int k=1;k<=n;k++)

               for(int i=1;i<k;i++)

                for(int j=i+1;j<k;i++)

               ans=min(ans,dis[i][k]+dis[k][j]+dis[i][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]);

    <8>并查集

    “并”:void unionn (int x,int y)

    {

             x=find(x);

             y=find(y);

             if(x!=y) 

             pre[x]=y; 

    }

     “查”:int find (int x)

    {

             if(pre[x]!=x)                  //路径压缩算法,如果不需要可以这样写

               pre[x]=find(pre[x]);       //if(x!=y) return find(pre[x]);

             return pre[x];               //else return x;

    }

    “集”:bool judge(int x,int y)  //判断是否连通,可以直接写在代码里不用另写函数。

    {

             x=find(x);             

             y=find(y);

             if(x==y) return true;

             else return false;

     }

    <9>MST(最小生成树)

    1.      prim算法

     #include<iostream>

    #include<cstring>

    #define maxn 5010

    #define maxm 200010

    using namespace std;

    int map[maxn][maxn];

    int e[maxm];

    int v[maxn];

    int n,x,y,w,m;

    int main()

    {

    memset(e,0x7f,sizeof(e));

    cin>>n>>m;

    for(int i=1;i<=m;i++)

    {

               cin>>x>>y>>w;

               map[x][y]=map[y][x]=w;

    }

    e[1]=0;

    for(int i=1;i<=n;i++)

    {

               int k=0;

               for(int j=1;j<=n;j++)

                if((v[j]==0) && (e[j]<e[k]))

                  k=j;

               v[k]=1;

               for(int j=1;j<=n;j++)

                if((v[j]==0) && (map[k][j]<e[j]))

                  e[j]=map[k][j];

    }

    int sum=0;

    for(int i=1;i<=n;i++)

     sum+=e[i];

    cout<<sum<<endl;

    return 0;

    }

    2.      kruskal算法

    #include<iostream>

    #include<algorithm>

    #define maxn 5010

    #define maxm 200010

    using namespace std;

    int n,k,m,sum,pre[maxn];

    int find (int x);

    struct point

    {

             int x;

             int y;

             int v;

    }a[maxm];

    void unionn (int x,int y)

    {

             x=find(x);

             y=find(y);

             if(x!=y) 

             pre[x]=y; 

    }

    int find (int x)

    {

             if(pre[x]!=x)          

               pre[x]=find(pre[x]);  

             return pre[x];     

    }

    int cmp(const point &a,const point &b)

    {

             if(a.v<b.v)return 1;

             else return 0;

    }

    int main()

    {

             cin>>n>>m;

             for(int i=1;i<=m;i++)

             {

                       cin>>a[i].x;

                       cin>>a[i].y;

                       cin>>a[i].v;

             }

             for(int i=1;i<=n;i++)

               pre[i]=i;

             sort(a+1,a+1+m,cmp);

             for(int i=1;i<=m;i++)

             {

                       if(find(a[i].x)!=find(a[i].y))

                       {

                                unionn(a[i].x,a[i].y);

                                sum+=a[i].v;

                                k++;

                       }

                       if(k==n-1) break;

             }

             cout<<sum<<endl;

             return 0;

    }

    <10>拓扑排序

    #include<iostream>

    using namespace std;

    int a[101][101],c[101],r[101],ans[101];

    int i,j,tot,temp,num,n,m;

    int main()

    {

             cin>>n;

             for(int i=1;i<=n;i++)

             {

                       do

                       {

                                cin>>j;

                                if(j!=0)

                                {

                                         c[i]++;

                                         a[i][c[i]]=j;

                                         r[j]++;

                                }

                       }while(j!=0);

             }

             for(int i=1;i<=n;i++)

              if(r[i]==0)

               ans[++tot]=i;

             do

             {

                       temp=ans[tot];

                       cout<<temp<<" ";

                       tot--;

                       num++;

                       for(int i=1;i<=c[temp];i++)

                       {

                                r[a[temp][i]]--;

                                if(r[a[temp][i]]==0)

                                 ans[++tot]=a[temp][i];

                       }

             }while(num!=n);

             return 0;

    }

    <11>关键路径

    1.      求出每个点的最早发生的时间,正序求得

    Earliest[1n]=max(Earliest[1n+map[pre][ 1n])

    2.      求出每个点最迟发生的时间,逆序求得

    Last[n1]=min(Last[n1]-map[n1][pre])

    3.      最迟-最早的的余量如果为0,则此点为关键点,项连得路径叫做关键路径。

    1.      数论

     <1>辗转相除法

     a / b = b / a%b

    int gcd (int a,int b)

    {

          if(b==0)

          return a;

          else

          gcd(b,a%b);

     }

    也可以用来求最小公倍数,lcm=a/gcd(a,b)*b。先除后乘的原因是为了避免乘法溢出。

    2.唯一分解定理:一个数可以分成若干个素数相乘的形式。

    3.      素数定理:π(x)~x/lnx.求出有多少个小于x的素数。

    4.      Eratosthenes筛素数

     Int m=sqrt(n+0.5);

     Forint i=1;i<=n;i++

      If(vis[i]==0)

       For(int j=i*i;j<=n;j++)

    Vis[j]=1;

    还要确定1不是素数,2是素数。如果标记为1i不为素数,如果未标记则为素数。

    5.      扩展欧几里得算法

     Void gcd (int a,int b,int &d,int &x,int &y)

    {

      If(b==0)  {d=a; x=1; y=0;}

      Else { gcd(b,a%b,y,x);

            Y-=x*(a/b);

           }

    }

    求出一组解来,其他部分可写为(x0+kb’,y0-ka’),a’=a/gcd(a,b),b’=b/gcd(a,b);k是整数。

    G=gcd(g/b)  ax+bx=c c不为g的倍数是,则此方程无整数解。

    6.      幂取模

     a^n %m 的值:

     Int mod (int a,int b,int m)

    {

    If(n==0) return 1;

     Int x=mod(a,n/2,m);

     Long long ans=(long long)x*x %m;

    If(n%2==1) ans=ans*a % m;

    Return (int) ans;

    }

    7.      同余与模运算:

      (a+b)%n=((a %n)+(b%n))%n

      (a-b)%n=((a%n)-(b%n)+n)%n

      ab%n=(a%n)(b%n)%n

    8.      杨辉三角:

                          1

                       1    1

                      1   2   1

                   1    3    3   1

                 1    4   6    4    1

               1   5    10  10   5    1  

             1   6   15    20  15   6    1

     

    C[i]=c[i+1]*(n-i+1)/i;

    C[i]=n!/i!(n-1)!;

  • 相关阅读:
    GitLab 介绍
    git 标签
    git 分支
    git 仓库 撤销提交 git reset and 查看本地历史操作 git reflog
    git 仓库 回退功能 git checkout
    python 并发编程 多进程 练习题
    git 命令 查看历史提交 git log
    git 命令 git diff 查看 Git 区域文件的具体改动
    POJ 2608
    POJ 2610
  • 原文地址:https://www.cnblogs.com/srpihot/p/6305462.html
Copyright © 2011-2022 走看看