复习2

1.最小生成树

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,num,head[5010],ans,dis[5010];
bool vis[5010];
struct node{
    int to,v,pre;
}e[400010];
void Insert(int from,int to,int v){
    e[++num].pre=head[from];
    e[num].to=to;
    e[num].v=v;
    head[from]=num;
}
void Prim(){
    memset(dis,127/3,sizeof(dis));
    dis[1]=0;
    for(int i=1;i<=n;i++){
        int minn=0x7fffffff,k=0;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dis[j]<minn){
                minn=dis[j];
                k=j;
            }
        }
        if(minn==0x7fffffff)break;
        vis[k]=1;
        for(int j=head[k];j;j=e[j].pre){
            int f=e[j].to;
            if(!vis[f])
                dis[f]=min(dis[f],e[j].v);
        }
    }
    for(int i=1;i<=n;i++)ans+=dis[i];
}

int main(){
    scanf("%d%d",&n,&m);
    int x,y,z;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        Insert(x,y,z);Insert(y,x,z);
    }
    Prim();
    printf("%d",ans);
}
Prim算法
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,num,head[5010],fa[5010];
struct node{
    int x,y,z;
}E[200010];
bool cmp(node a,node b){
    return a.z<b.z;
}
int find(int x){
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    int x,y,z,cnt=0,ans=0;
    for(int i=1;i<=m;i++)scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].z);
    sort(E+1,E+m+1,cmp);
    for(int i=1;i<=m;i++){
        int f1=find(E[i].x),f2=find(E[i].y);
        if(f1!=f2){
            fa[f1]=f2;
            ans+=E[i].z;
            cnt++;
        }
        if(cnt==n-1)break;
    }
    if(cnt<n-1)puts("orz");
    else printf("%d",ans);
}
kruskal算法

2.最短路

struct Node{
    int id,dist;
    bool operator < (const Node b)const{
        return dist>b.dist;
    }
};
bool Dij(int limit){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    priority_queue<Node>q;
    q.push(make_node(s,0));
    dis[s]=0;
    while(!q.empty()){
        Node cur=q.top();q.pop();int now=cur.id;
        if(w[now]>limit)continue;
        if(vis[now])continue;
        vis[now]=1;
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(w[to]>limit)continue;
            if(dis[to]>dis[now]+e[i].v){
                dis[to]=dis[now]+e[i].v;
                q.push(make_node(to,dis[to]));
            }
        }
    }
    return dis[t]<=g;
}
堆优化Dij
bool spfa(int limit){
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    deque<int>q;
    q.push_back(s);
    dis[s]=0;vis[s]=1;
    while(!q.empty()){
        int now=q.front();q.pop_front();vis[now]=0;
        if(w[now]>limit)continue;
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(w[to]>limit)continue;
            if(dis[to]>dis[now]+e[i].v){
                dis[to]=dis[now]+e[i].v;
                if(!vis[to]){
                    vis[to]=1;
                    if(!q.empty()&&dis[to]<dis[q.front()])q.push_front(to);
                    else q.push_back(to);
                }
            }
        }
    }
    return dis[t]<=g;
}
SLF优化spfa

3.匈牙利算法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 1000010
int n,link[maxn],num,head[maxn*2];
bool vis[maxn];
struct node{
    int to,pre;
}e[maxn*2];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
bool dfs(int x){
    for(int i=head[x];i;i=e[i].pre){
        int to=e[i].to;
        if(!vis[to]){
            vis[to]=1;
            if(!link[to]||dfs(link[to])){
                link[to]=x;return 1;
            }
        }
    }
    return 0;
}
int main(){
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        if(x<=n)Insert(x,i);
        if(y<=n)Insert(y,i);
    }
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        if(!dfs(i)){
            printf("%d",i-1);
            return 0;
        }
    }
    printf("%d",n);
    return 0;
}
最大匹配数

4.Tarjan算法(强联通分量,桥,割)

无向图割点
对该图进行一次 Tarjan 算法(这里注意在搜索树中把无向边当做有向边看。即LOW[u]=min(LOW[u],DFN[v])(v 是 u 的祖先)的条件变为(v 是 u 的祖先且 v 不是 u 的父亲))这样之后枚举搜索树上的所有边(u,v),若存在 LOW[v]>=DNF[u],则 u 是割点。
无向图割边
对该图进行一次 Tarjan 算法(这里注意在搜索树中把无向边当做有向边看。即LOW[u]=min(LOW[u],DFN[v])(v 是 u 的祖先)的条件变为(v 是 u 的祖先且 v 不是 u 的父亲))这样之后枚举搜索树上的所有边(u,v),若存在 LOW[v]>DNF[u],则(u,v)为割边。

#include<iostream>
#include<cstdio>
#define MAXn 100000
#define MAXm 2000000
using namespace std;
int dfn[MAXn],low[MAXn],head[MAXm],st[MAXn],belong[MAXn];
bool in_st[MAXn];
int ans,n,m,num,s_num,cnt,group_num;
struct node{
    int to,pre;
}e[MAXm];
void Insert(int from,int to){
    e[++num].pre=head[from];
    e[num].to=to;
    head[from]=num;
}
void group(int u){
    cnt++;st[++s_num]=u;dfn[u]=low[u]=cnt;in_st[u]=1;
    for(int i=head[u];i;i=e[i].pre){
        int v=e[i].to;
        if(!dfn[v]){
            group(v);
            if(low[v]<low[u])low[u]=low[v];
        }
        else if(dfn[v]<low[u])
            if(in_st[v])
            low[u]=dfn[v];
    }
    if(dfn[u]==low[u]){
        group_num++;
        while(st[s_num]!=u){
            in_st[st[s_num]]=0;
            belong[st[s_num]]=group_num;
            s_num--;
        }
        in_st[u]=0;s_num--;
        belong[u]=group_num;
    }
}
int main(){
    freopen("Tarjan_group.txt","r",stdin);
    scanf("%d%d",&n,&m);int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i])group(i);
    }
    for(int i=1;i<=n;i++){
        printf("%d ",belong[i]);
    }printf("
%d",group_num);
}
Tarjan_group模板
#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 100010
using namespace std;
int dfn[maxn],low[maxn],cnt,ans;
bool cut[maxn];
int n,m,num,head[maxn];
struct node{
    int from,to,pre;
}e[maxn*2];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].from=from;
    e[num].pre=head[from];
    head[from]=num;
}
void Tarjan(int u,int father){
    int rc=0;
    cnt++;dfn[u]=low[u]=cnt;
    for(int i=head[u];i;i=e[i].pre){
        int v=e[i].to;
        if(!dfn[v]){
            Tarjan(v,father);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=father)cut[u]=1;
            if(u==father)rc++;
        }
        low[u]=min(low[u],dfn[v]);
    }
    if(u==father&&rc>=2)cut[u]=1;
}
int main(){
    freopen("Cola.txt","r",stdin);
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);Insert(y,x);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])Tarjan(i,i);
    for(int i=1;i<=n;i++)if(cut[i])ans++;
    printf("%d
",ans);
    for(int i=1;i<=n;i++)if(cut[i])printf("%d ",i);
}
Tarjan割点
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<vector>
#define N 201
vector<int>G[N];
int n,m,low[N],dfn[N];
bool is_cut[N];
int father[N];
int tim=0;
void input()
{
    scanf("%d%d",&n,&m);
    int a,b;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&a,&b);
        G[a].push_back(b);/*邻接表储存无向边*/
        G[b].push_back(a);
    }
}
void Tarjan(int i,int Father)
{
    father[i]=Father;/*记录每一个点的父亲*/
    dfn[i]=low[i]=tim++;
    for(int j=0;j<G[i].size();++j)
    {
        int k=G[i][j];
        if(dfn[k]==-1)
        {
            Tarjan(k,i);
            low[i]=min(low[i],low[k]);
        }
        else if(Father!=k)/*假如k是i的父亲的话,那么这就是无向边中的重边,有重边那么一定不是桥*/
            low[i]=min(low[i],low[k]);
    }
}
void count()
{
    int rootson=0;
    Tarjan(1,0);
    for(int i=2;i<=n;++i)
    {
        int v=father[i];
        if(v==1)
        rootson++;/*统计根节点子树的个数,根节点的子树个数>=2,就是割点*/
        else{
            if(low[i]>=dfn[v])/*割点的条件*/
            is_cut[v]=true;
        }
    }
    if(rootson>1)
    is_cut[1]=true;
    for(int i=1;i<=n;++i)
    if(is_cut[i])
    printf("%d
",i);
    for(int i=1;i<=n;++i)
    {
        int v=father[i];
        if(v>0&&low[i]>dfn[v])/*桥的条件*/
        printf("%d,%d
",v,i);
    }
    
}
int main()
{
    input();
    memset(dfn,-1,sizeof(dfn));
    memset(father,0,sizeof(father));
    memset(low,-1,sizeof(low));
    memset(is_cut,false,sizeof(is_cut));
    count();
    return 0;
}
求桥,割点

5.lca

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500010
using namespace std;
int n,q,root,fa[maxn][22],dep[maxn],num,head[maxn];
struct node{int to,pre;}e[maxn*2];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
void dfs(int now,int father){
    fa[now][0]=father;
    dep[now]=dep[father]+1;
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        if(to==father)continue;
        dfs(to,now);
    }
}
int lca(int a,int b){
    if(dep[a]!=dep[b]){
        if(dep[a]<dep[b])swap(a,b);
        for(int i=18;i>=0;i--)
            if(dep[fa[a][i]]>=dep[b])a=fa[a][i];
    }
    if(a==b)return a;
    for(int i=18;i>=0;i--)
        if(fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];
    if(a==b)return a;
    return fa[a][0];
}
/*int get(int a,int delta){
    for(int i=0;i<=21;i++){
        if(delta&(1<<i))a=fa[a][i];
    }return a;
}
int lca(int a,int b){
    if(dep[a]<dep[b])swap(a,b);
    a=get(a,dep[a]-dep[b]);
    if(a==b) return a;
    for(int i=21;i>=0;i--)
        if(fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}*/
int main(){
    scanf("%d%d%d",&n,&q,&root);
    int x,y;
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);Insert(y,x);
    }
    dfs(root,root);
    for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    while(q--){
        scanf("%d%d",&x,&y);
        printf("%d
",lca(x,y));
    }
}
倍增lca(两种跳法均可)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 1000010
int n,s1,s2,head[maxn*2],num,son[maxn],sz[maxn],top[maxn],fa[maxn],dep[maxn];
struct node{
    int to,pre;
}e[maxn*2];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
void dfs1(int u,int father){
    sz[u]=1;
    fa[u]=father;
    dep[u]=dep[father]+1;
    for(int i=head[u];i;i=e[i].pre){
        int v=e[i].to;
        if(v==father)continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(!son[u]||sz[v]>sz[son[u]])son[u]=v;
    }
}
void dfs2(int u,int father){
    top[u]=father;
    if(son[u])dfs2(son[u],father);
    else return;
    for(int i=head[u];i;i=e[i].pre){
        int v=e[i].to;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]<dep[y])return x;
    else return y;
}
int main(){
    freopen("Cola.txt","r",stdin);
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        Insert(y,x);
        Insert(x,y);
    }
    scanf("%d%d",&s1,&s2);
    dfs1(1,0);
    dfs2(1,1);
    printf("%d",lca(s1,s2));
}
树剖lca

6.树上差分

7.欧拉回路

定义:给定无孤立结点图G,若存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路,如果存在一条回路经过G每条边有且仅有一次,称这条回路为欧拉回路。具有欧拉回路的图成为欧拉图。

欧拉回路存在的充要条件: 每个点的度为偶数(无向图) 每个点的入度出度相等(有向图)

欧拉路存在的必要条件: 有且仅有两个点的度为奇数(无向图) 总的入度和等于总的出度和,有且仅有两个点的入度、出度差为1,其他点相等(有向图)

void eular(int x){
    for(int i=1;i<=52;i++){
        if(cxt[x][i]){
            cxt[x][i]=cxt[i][x]=0;
            eular(i);
        }
    }
    s[++cnt]=x;
}

8.拓扑排序

原文地址:https://www.cnblogs.com/thmyl/p/7792402.html