【模板】图论

一、生成树

洛谷模板最小生成树【跑的还算快的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5020
#define M 200008
using namespace std;

int n,m,ans;

int tot;

int fa[N];

struct E{
    int x,y,z;
}e[M];

bool cmp(E a,E b){
    return a.z<b.z;
}

int f(int x){
    return fa[x]==x?x:fa[x]=f(fa[x]);
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
    for(int i=1;i<=n;i++)fa[i]=i;
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;i++){
        int x=e[i].x,y=e[i].y;
        int fx=f(x),fy=f(y);
        if(fx!=fy){
            fa[fx]=fy;
            ans+=e[i].z;
            if(++tot==n-1)break;
        }
    }
    if(tot!=n-1)cout<<"orz\n";
    else printf("%d\n",ans);
    return 0;
}

kruskal

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 5020
#define M 200009
#define inf 10000000
using namespace std;

int n,m,sumedge,ans,dis[N],head[N];

bool vis[N];

struct Edge{
    int x,y,z,nxt;
    Edge(int x=0,int y=0,int z=0,int nxt=0):
        x(x),y(y),z(z),nxt(nxt){}
}edge[M<<1];

void add(int x,int y,int z){
    edge[++sumedge]=Edge(x,y,z,head[x]);
    head[x]=sumedge;
}

void prim(){
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    for(int i=1;i<=n;i++){
        int u=-1,mn=inf;
        for(int j=1;j<=n;j++){
            if(dis[j]<mn&&!vis[j]){
                u=j;mn=dis[j];
            }
        }
        if(u==-1){
            ans=-1;return;
        }
        vis[u]=true;ans+=dis[u];
        for(int x=head[u];x;x=edge[x].nxt){
            int v=edge[x].y;
            if(dis[v]>edge[x].z)dis[v]=edge[x].z;
        }
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }
    prim();
    if(ans==-1)cout<<"orz\n";
    else cout<<ans;
    return 0;
}

prim

二、最短路

memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=n;i++)dis[i][i]=0;
for(int k=1;k<=n;k++)
 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]);

floyed

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 10009
#define inf 2147483647
using namespace std;

int n,m,s,sumedge;
int dis[maxn],vis[maxn],head[maxn];

struct Edge{
    int x,y,z,nxt;
    Edge(int x=0,int y=0,int z=0,int nxt=0):
        x(x),y(y),z(z),nxt(nxt){}
}edge[maxn*51];

void add(int x,int y,int z){
    edge[++sumedge]=Edge(x,y,z,head[x]);
    head[x]=sumedge;
}

void di(){
    for(int i=1;i<=n;i++)dis[i]=inf;
    dis[s]=0;
    for(int i=1;i<=n;i++){
        int k,mn=inf;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dis[j]<mn){
                mn=dis[j];k=j;
            }
        }
        if(mn==inf)break;
        vis[k]=true;
        for(int j=head[k];j;j=edge[j].nxt){
            int v=edge[j].y;
            if(!vis[v]&&dis[v]>dis[k]+edge[j].z)
             dis[v]=dis[k]+edge[j].z;
        }
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    di();
    for(int i=1;i<=n;i++)printf("%d ",dis[i]);
    return 0;
}

没有优化的dijkstra

//waiting

void spfa(){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    while(!q.empty())q.pop();
    dis[1]=0;vis[1]=true;q.push(1);
    while(!q.empty()){
        int now=q.front();q.pop();vis[now]=false;
        for(int i=head[now];i;i=edge[i].nxt){
            int v=edge[i].y;
            if(dis[v]>dis[now]+edge[i].z){
                dis[v]=dis[now]+edge[i].z;
                if(!vis[v]){
                    vis[v]=true;q.push(v);
                }
            }
        }
    }
}

spfa

三、二分图

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1020
using namespace std;

int n,m,e,ans,sumedge;

int vis[N],match[N],head[N];

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[N*N];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

bool dfs(int x){
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(!vis[v]){
            vis[v]=true;
            if(match[v]==0||dfs(match[v])){
                match[v]=x;return true;
            }
        }
    }
    return false;
}

int main(){
    scanf("%d%d%d",&n,&m,&e);
    for(int i=1;i<=e;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        if(y>m||x>n)continue;
        add(x,y);
    }
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        if(dfs(i))ans++;
    }
    cout<<ans<<endl;
    return 0;
}

洛谷模板二分图匹配

封锁阳光大学

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 10001
#define maxm 100009
#ifdef WIN32
#define PLL "%I64d\n"
#else
#define PLL "%lld\n"
#endif
using namespace std;

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[maxm*2];

int head[maxn],col[maxn],q[maxn];
int sumedge,n,m,cnt1,x,y,cnt2,ans;

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    memset(col,-1,sizeof(col));
    for(int i=1;i<=n;i++){
        if(col[i]==-1){
            int h,t;h=t=1;col[i]=1;q[h]=i;
            while(h<=t){
                int now=q[h++];
                 if(col[now]==1)cnt1++; else cnt2++;
                for(int j=head[now];j;j=edge[j].nxt){
                    int v=edge[j].y;
                    if(col[v]==-1){
                        q[++t]=v;
                        col[v]=col[now]^1;
                    }
                    if(col[v]==col[now]){
                        printf("Impossible");
                        return 0;
                    }
                }
            }
        }
        ans+=min(cnt1,cnt2);
        cnt1=cnt2=0;
    }
    printf(PLL,ans);
}

二分图判定

四、LCA

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500009
using namespace std;

int n,m,s,sumedge;
int head[maxn],deep[maxn],size[maxn],dad[maxn],top[maxn];

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[maxn<<1];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

void dfs(int x){
    size[x]=1;deep[x]=deep[dad[x]]+1;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(v==dad[x])continue;
        dad[v]=x;dfs(v);
        size[x]+=size[v];
    }
}

void dfs_(int x){
    int s=0;
    if(!top[x])top[x]=x;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(v!=dad[x]&&size[v]>size[s])s=v;
    }
    if(s){
        top[s]=top[x];
        dfs_(s);
    }
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(v!=dad[x]&&v!=s)dfs_(v);
    }
}

int lca(int x,int y){
    for(;top[x]!=top[y];){
        if(deep[top[x]]>deep[top[y]])swap(x,y);
        y=dad[top[y]];
    }
    if(deep[x]>deep[y])return y;
    return x;
}

int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(s);dfs_(s);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
}

树剖

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500009
using namespace std;

int n,m,s,sumedge;
int head[maxn],dad[maxn][25],deep[maxn];

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[maxn<<1];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

void dfs(int x){
    deep[x]=deep[dad[x][0]]+1;
    for(int i=0;dad[x][i];i++)
     dad[x][i+1]=dad[dad[x][i]][i];
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(v==dad[x][0])continue;
        dad[v][0]=x;
        dfs(v);
    }
}

int lca(int x,int y){
    if(deep[x]>deep[y])swap(x,y);
    for(int i=20;i>=0;i--)if(deep[dad[y][i]]>=deep[x])y=dad[y][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(dad[x][i]!=dad[y][i])x=dad[x][i],y=dad[y][i];
    return dad[x][0];
}

int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(s);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
}

倍增

五、tarjan

#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define maxn 1000006

int n,m,sumedge,root,ans,tim;
int low[maxn],dfn[maxn],iscut[maxn],head[maxn]; 

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[maxn<<1];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

int min(int x,int y){
    return x<y?x:y;
}

void Tarjian(int x,int fa){
    dfn[x]=low[x]=++tim;int cnt=0;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(v==fa)continue;
        if(dfn[v]==0){
            cnt++;
            Tarjian(v,x);
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x]&&x!=root&&iscut[x]==0)iscut[x]=true,ans++;
            if(x==root&&cnt>=2&&iscut[x]==0)iscut[x]=true,ans++;
        }else low[x]=min(low[x],dfn[v]);
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
     if(!dfn[i])root=i,Tarjian(i,i);
    printf("%d\n",ans);
    for(int i=1;i<=n;i++) if(iscut[i]) printf("%d ",i);
    return 0;
}

割点

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 22000
using namespace std;

int n,sumedge,sumclr,top,tim,ans;
int Stack[maxn],instack[maxn],low[maxn],dfn[maxn],bel[maxn],rd[maxn],head[maxn];

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[maxn<<1];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

void Tarjian(int x){
    Stack[++top]=x;instack[x]=true;
    low[x]=dfn[x]=++tim;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(instack[v])low[x]=min(low[x],dfn[v]);
        else if(!dfn[v]){
            Tarjian(v);
            low[x]=min(low[x],low[v]);
        }
    }
    if(low[x]==dfn[x]){
        sumclr++;
        while(Stack[top+1]!=x){
            bel[Stack[top]]=sumclr;
            instack[Stack[top]]=false;
            top--;
        }
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;
        while(1){
            scanf("%d",&x);
            if(!x)break;
            add(i,x);
        }
    }
    for(int i=1;i<=n;i++)if(!dfn[i])Tarjian(i);
    for(int x=1;x<=n;x++){
        for(int i=head[x];i;i=edge[i].nxt){
            int v=edge[i].y;
            if(bel[x]!=bel[v])rd[bel[v]]++;
        }
    }
    for(int i=1;i<=sumclr;i++)if(!rd[i])ans++;
    printf("%d\n",ans);
    return 0;
}

AC

缩点

注意图不一定连通

六、结论/性质

二分图:

1.  最小点覆盖  = 最大匹配。

2. 最大独立集  = 顶点总数 - 最大匹配。

3. 最小边覆盖 = 顶点总数 - 最大匹配。

4. 最小路径覆盖 = 顶点总数 - 最大匹配。

1、图上两点之间的路径的最小边的最大值一定在最大生成树上

2、树的直径:树上最远两点的距离

3、树的重心:

树的重心定义

树中的一个点,删掉这个点,使得剩下的树所

构成的森林中最大的子树节点数最少。

树的重心推论

1.设树上的一个点S,树上其余所有点到S点的

距离之和最小,那么S就是重心。

2.树的重心不唯一。

然后可以依靠定义来求树的重心。

首先以任意一个点,进行dfs,dfs过程中统计以

每个点为根的子树中,一共含有多少节点。

然后 总节点数 减去 子树的节点数 减去1(自己),

就是它父亲那边点的数量。做差之后取最小值。

时间: 2024-10-12 18:42:40

【模板】图论的相关文章

模板-图论

图论算法相关模版, 可能有错误, 省选前持续更正中 重要的不是模版内容, 而是提供算法的实现思路.

模板 - 图论 - 树 - 最近公共祖先

令 $f[i][j]$ 表示 $i$ 的 $2^j$ 辈祖先, $f[i][0]$ 就表示 $i$ 的父节点. 可以得到状态转移方程 $f[i][j]=f[f[i][j-1]][j-1]$ .当没有 $2^j$ 辈祖先时 $f[i][j]=0$ 一遍 DFS 计算即可 void dfs(int u, int father) { dep[u] = dep[father] + 1; // dep[x] 表示 x 的深度,在查询时会用到 for (int i = 0; i <= 19; i++) f[

模板 - 图论 - 基环树

基环树也可以直接套强连通缩点给秒了,但是事实上假如不需要缩点的话有更简单的写法. 下面是一种示例,必须是内向基环树,注意内向基环树的dfs上面有好几个时点: 0.进入环的时候,有时是从入度为0的点进入可能会有特殊操作,但是一般来说进入的时候主要是各个操作的初始化值. 1.当 color[u]!=0&&color[u]==c 时,第一次找到环的入口,可以这时候处理入口的值,但一般只交给3去做就好了. 2.当 color[u]!=0&&color[u]!=c 时,意味着找到其他

模板 - 图论 - 图的存储和遍历

链式前向星法存的带边权的图,(尤其在多组数据时)时间效率比vector略高且节省空间,缺点是不容易对一个点的出边进行排序去重,当平行边无所谓时选择这个方法是非常明智的.链式前向星法存图的最大的问题是要记得给反向边预留空间. 图的存储和遍历,在图中搜索树的父子关系其实一般不是很重要.注意下面的代码是没有对vis进行清空的,因为其实并不是每次搜索前都会需要清空,有时候有一些其他的操作(特别是有向图).需要管边权的去找dijkstra算法就好了. struct Graph { static const

模板 - 图论 - 最近公共祖先 - 倍增算法 - LCA

const int MAX = 100000; vector <int> G[MAXN + 5]; int dep[MAXN + 5], fa[MAXN + 5][20 + 1]; void dfs(int u, int p) { dep[u] = depth[p] + 1; fa[u][0] = p; for (int i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1]; for(auto &v : G[u]) { i

POJ1966(Cable TV Network)

题目链接:传送门 题目大意:给你一副无向图,求解图的顶点连通度 题目思路:模板(图论算法理论,实现及应用 P396) Menger定理:无向图G的顶点连通度k(G)和顶点间最大独立轨数目之间存在如下关系: 1.若G是完全图,k(G)=|V(G)|-1 2.若G不是完全图,k(G)=min{P(A,B)}  其中A,B不直接相连 #include <iostream> #include <cstdio> #include <cstdlib> #include <cm

题解 P2792 【[JSOI2008]小店购物】

题目链接 Solution [JSOI2008]小店购物 题目大意:有若干件物品,每个物品有一个原价,购买某件物品后可以以更优价购买另一件物品.每件物品有一个需求数目,既不能多买,也不能少买(如果需求\(0\)件你就不能买,哪怕可能使得总价最优) 题目分析:看到题解区巨佬的题解,发现此题有一个绝妙的贪心做法. 对于某件物品,我们怎样使得购买它的代价最小呢?我们可以贪心的在这件物品所有的可行方案(原价与优惠价)里面取最小的,做一次乘法运算即可得出答案 关键是,怎样使得这个贪心是正确的呢?我们发现,

洛谷P3385 【模板】负环 DFS-SPFA 判负环 图论

洛谷P3385 [模板]负环 图论 今天get了 一个 DFS-SPFA 判负环的方法 一般的 BFS-SPFA 判负环 一般就是 不停地做,如果某点第 n+1次加入队列中,那么说明这个图存在负环然而我并不会证明,期望复杂度是 O(kM) k 大约是在 2 左右 但是其实对于一些极限数据,最坏可以把他卡到 O( NM) 额,这就直接炸飞了是不是,而且据说,一些数据比较强的题目,总会想到卡一卡SPFA的, 然后我们换一种思路 因为题目中一定存在一种 负环对吧,所以说假如你某段路径权值和为自然数的时

Dijkstra模板题图论书p133

#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include <list> #include <

图论 --- spfa + 链式向前星 (模板题) dlut 1218 : 奇奇与变形金刚

1218: 奇奇与变形金刚 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 130  Solved: 37[Submit][Status][Web Board] Description 奇奇 gigi 奇奇口头禅:别人的失败就是我的快乐! 星座:处女座 生日:8月25日 血型:不明 年龄:2岁 生肖:鸡 身高:120公分 体重:149公斤 职业:机器人 兴趣:周游世界 宠物:变形金刚 最喜欢:充电 最讨厌:拔掉它的电源插头 偶像:科学怪人 语言:中文