圖論【模板】

加了部分博客鏈接

1.圖的存儲:

鄰接矩陣: g[a][b]存儲點a,b間的有關信息(權值或該兩點間是否有邊)稠密圖
鄰接表: 類似哈希表的拉鍊法,每個點都有一個單鏈表,存儲這個點可以走到的點(包括直接走到和間接走到)。
鄰接表的結構體實現

struct node{
    int value;//存儲邊的權值
    int to;//存儲該邊的終點
    int next;//存儲下一條邊的編號
}a[N];
int cnt=0;
int head[N];//存儲以i爲起點的邊的編號
void add(int u,int v,int value){
    a[cnt].to=v;
    a[cnt].value=value;
    a[cnt].next=head[u];
    head[u]=cnt++;//以當前點爲起點的編號爲cnt
    //無向圖,正反各存一遍
    a[cnt].to=u;
    a[cnt].value=value;
    a[cnt].next=head[v];
    head[v]=cnt++;
}

鏈式前向星:

int h[N],e[N],ne[N],idx;
void init(){
    idx=0;
    memset(h,-1,sizeof h);
}
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}

2.圖的深度優先遍歷和廣度優先遍歷

3.拓撲排序

有向無環圖=拓撲圖
手寫隊列(容易出鍋)

int h[N],e[N],ne[N],idx,ans=N,n,m;
int d[N],q[N];
void init(){
    idx=0;
    memset(h,-1,sizeof h);
}
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort(){
    int hh=0,tt=-1;
    for(int i=1;i<=n;i++)
        if(!d[i]) q[++tt]=i;
    while(hh<=tt){
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(--d[j]==0) q[++tt]=j;
        }
    }
    return tt==n-1;
}

stl queue寫法

4.最短路算法

最短路問題分爲單源最短路和多源匯最短路。前者是求某一點到其他點的最短距離,後者是起點終點不確定,任選一組起點終點求最短路。
n點m邊

Dijkstra
所有邊權均是正數的單源最短路 O(nn)
適用於稠密圖 即m和n
n接近

int n,m;///n個點 m條邊
int g[maxn][maxn];///鄰接矩陣建圖
int dis[maxn];///記錄當前點到起點的距離
bool st[maxn];///若當前點的最短距離已經確定 爲true

int dijkstra(){
    memset(dis,0x3f,sizeof dis);///初始化距離爲正無窮
    dis[1]=0;///從1開始更新
    for(int i=0;i<n-1;i++){
        int t=-1;
        ///找到當前不在st中而且距離最小的點t
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dis[t]>dis[j]))
                t=j;
        ///用點t更新其他不在st中的點的距離
        for(int j=1;j<=n;j++)
            dis[j]=min(dis[j],dis[t]+g[t][j]);
        ///將t點放到st集合裏
        st[t]=true;
    }
    ///如果該點距離爲正無窮 說明沒有更新過
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}

堆優化的Dijkstra
所有邊權均是正數的單源最短路 O(mlogn)
適用於稀疏圖 即m和n一個數量級

int n,m;
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
int dis[maxn];
bool st[maxn];

void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    ///建立一個維護最小值的優先隊列
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    heap.push({0,1});///起始點放入隊列
    while(heap.size()){
        auto t=heap.top();///最小值
        heap.pop();
        int ver=t.second,d=t.first;
        if(st[ver]) continue;///該點更新
        st[ver]=true;
        for(int i=h[ver];i!=-1;i=ne[i]){
            int j=e[i];
            if(dis[j]>d+w[i]){
                dis[j]=d+w[i];
                heap.push({dis[j],j});
            }
        }
    }
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}

Bellman Ford
存在負權邊/限制邊數 O(n*m)
適合於m和n一個數量級

struct node{
    int a,b,c;
}e[maxx];
int n,m,k;
int dis[maxn],last[maxn];///保證更新只是從上一次更新,不會被以前的影響 
void BellmanFord(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    for(int i=0;i<k;i++){
        memcpy(last,dis,sizeof dis);
        for(int j=0;j<m;j++){
            auto t=e[j];
            dis[t.b]=min(dis[t.b],last[t.a]+t.c);
        }
    }
}

SPFA
存在負權邊 (相當於隊列優化的Bellman Ford)

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return dist[n];
}

Floyd
Floyd 多源匯 O(n^3) 最短路

int n,m,q;
int d[maxn][maxn];
void init(){
	for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j) d[i][j]=0;
            else d[i][j]=inf;
}
void Floyd(){
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}

4.最小生成樹算法

prim算法

int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim(){
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == INF) return INF;

        if (i) res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

kruskal
O(mlogm)


int n,m;
int root[maxn];
struct node{
    int a,b,w;
}a[maxn];
bool cmp(node a,node b){
    return a.w<b.w;
}
int Find(int x){
    if(x!=root[x]) root[x]=Find(root[x]);
    return root[x];
}
int kruskal(){
    sort(a,a+m,cmp);
    for(int i=1;i<=n;i++) root[i]=i;
    int res=0,cnt=0;
    for(int i=0;i<m;i++){
        int aa=a[i].a;
        int b=a[i].b;
        int w=a[i].w;
        aa=Find(aa),b=Find(b);
        if(aa!=b){
            root[aa]=b;
            res+=w;
            cnt++;
        }
    }
    if(cnt<n-1) return INF;
    else return res;
}

5.染色法判定二分圖

鏈式前向星存圖

#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int n,m;
int h[100010],e[maxn],ne[maxn],idx;
int col[100010];

void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool dfs(int u,int c){
    col[u]=c;
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(!col[j]){
            if(!dfs(j,3-c)) return 0;
        }
        else if(col[j]==c) return 0;
    }
    return 1;
}

int main(){
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--){
        int x,y;
        cin>>x>>y;
        add(x,y);add(y,x);///無向圖
    }
    bool flag=1;
    for(int i=1;i<=n;i++)
        if(!col[i]){
            if(!dfs(i,1)){
                flag=0;
                break;
            }
        }
    if(flag) puts("Yes");
    else puts("No");
    return 0;
}

鄰接矩陣存圖
偷的學長的(超小聲)

vector<int>v[maxn];///vector存個圖
int vis[maxn];///vis標記顏色
ll n,m;
bool dfs(int u,int c)
{
    vis[u]=c;
    int sz=v[u].size();
    for(int i=0;i<sz;i++)
    {
        int e=v[u][i];
        if(vis[e]==c) return false;
        if(!vis[e]&&!dfs(e,-c)) return false;
    }
    return true;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    for(int i=1;i<=n;i++)///圖可能不全是聯通的 判斷全部子圖均爲二分圖纔可以
        if(vis[i]==0&&!dfs(i,1)){
            printf("No");
            return 0;
        }
    printf("Yes\n");
    return 0;
}

模板來自AcWing

未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章