Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

图论 #1

Open
Bryce1010 opened this issue Apr 3, 2020 · 19 comments
Open

图论 #1

Bryce1010 opened this issue Apr 3, 2020 · 19 comments

Comments

@Bryce1010
Copy link
Owner

Bryce1010 commented Apr 3, 2020

倍增及其应用 _ 未知作者

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 3, 2020

二分图与匹配 黄哲威

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 4, 2020

浅谈一些树形问题 -- 高胜寒

树的遍历

宽度优先

  • 层次序遍历

在bfs基础上, 添加一个list保存每层的结果

    vector<vector<int>> levelOrder(Node* root) {
        if(!root) return {};
        vector<vector<int>> ans;
        queue<Node*> que;
        que.push(root);
        while(!que.empty())
        {
            vector<int> v;
            for(int i=que.size();i;i--)
            {
                //压入当前层
                Node* curr=que.front();
                que.pop();
                v.push_back(curr->val);
                for(Node* it:curr->children)
                    que.push(it);
            }
            ans.push_back(v);
        }
        return ans;
    }

深度优先

  • 前序遍历
  • 中序遍历
class Solution {
public:
    vector<int> ans;
    vector<int> inorderTraversal(TreeNode* root) {
        if(root != NULL) {
            inorderTraversal(root -> left);
            ans.push_back(root -> val);
            inorderTraversal(root -> right);
        }
        return ans;
    }
};
  • 后序遍历

树的存储

  • 邻接表
  • 有根树记录所有孩子
  • 有根树记录父亲节点
  • DFS 序

树问题的应用

树形动态规划

  • 将一棵树原问题转化为其子树上问题

  • 为每一个子树计算其最优值

  • 将最优值合并至有根树

  • SPOJ/MTREE

最近公共祖先

5种写法

  • 暴力算法
  • 倍增算法
  • dfs序算法
  • targan算法
  • 树链剖分算法

将一颗树剖成若干条链
每一个点到根都只经过log级别条链
对于每条链当做数列来做

image

  • SPOJ/ QTREE2
  • SPOJ OTOCI
  • SPOJ COT
  • SPOJ MINDIST

DFS序+数据结构

树链剖分

动态树

  • 动态维护一种剖分
  • 类别树链剖分
  • 使用SPLAY维护动态树

其他

总结

根据题意, 判断是哪一类树形问题, 具体问题具体分析
对于每一种类型的题目找出相应的解法

若需求路径上的问题, 优先考虑LCA法, 其次考虑dfs序, 最后树链剖分;
首选找出树与图所不同的地方, 将题目转化为一些其他模型的问题;

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 4, 2020

浅析二分图匹配在信息学竞赛中的应用_王俊

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 8, 2020

生成树和拓扑排序_黄哲威

并查集

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

void merge(int x,int y){
    int p=getroot(x),q=getroot(y);
    fa[p]=q;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)
        fa[i]=i;
    return 0;
}

并查集优化- 路径压缩

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

void merge(int x,int y){
    int p=getroot(x),q=getroot(y);
    fa[p]=q;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)
        fa[i]=i;
    return 0;
}

并查集优化- 按秩合并

深度更小的树指向深度更大的树

int fa[maxn],h[maxn];
int getroot(int x){
    return x==fa[x]?x:getroot(fa(x));
}

void merge(int x,int y){
    int p=getroot(x),q=getroot(y);
    if(h[p]>h[q])swap(p,q);
    fa[p]=q;h[q]++;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)
        fa[i]=i;
    return 0;
}

生成树

Kruskal

int n,m,ans;
struce edge{
    u,v,w;
}e[M];

bool cmp(edge a, edge b){
    return a.w<b.w;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        cin>>e[i].u>>e[i].v>>e[i].w;
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;++i){
        int p=getroot(e[i].u),q=getroot(e[i].v]);
        if(p!=q){
            fa[p]=q;
            ans+=e[i].w;
        }
    }
    return 0;
}

Prim 算法

拓扑排序

int top,q[N];

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        d[v]++;
    }
    for(int i=1;i<=n;++i)
        if(!d[i])
            q[++top]=i;
    while(top){
        int u=q[top];top--;
        for(int i=0;i<e[u].size();++i){
            int v=e[u][i];
            d[v]--;
            if(!d[v])
                q[++top]=v;
        }
    }    
    return 0;
}
  • 团伙

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 9, 2020

图论复习 _ 未知作者

图的存储形式:

  • 邻接矩阵
  • 邻接链表

传递闭包

最小生成树

  • Kruskal 算法
    适用于稀疏图
  • Prim算法
    适用于稠密图

最短路的算法

  • Floyd算法 非负有向图

  • Dijkstra算法 非负 有向图

  • Bellman-Ford算法/ SPFA算法 实数, 不存在负环回路

二分图匹配

  • 二分图的最大匹配 (匈牙利算法)
  • 图的最小覆盖问题

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 9, 2020

图论入门与最短路 _ 黄哲威

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
vector<pa>v;
int main(){
    for(int i=1;i<=10;++i)
        v.push_back(make_pair(rand(),i));
    sort(v.begin(),v.end());
    for(int i=0;i<v.size();++i)
        cout<<v[i].first<<' '<<v[i].second<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;

priority_queue<int,vector<int>,greater<int> >q; //小根堆  
int main(){
    for(int i=1;i<=10;++i)
        q.push(rand());
    for(int i=1;i<=10;++i){
        cout<<q.top()<<endl;
        q.pop();
    }
    return 0;
}
  • 图的链表存储
vector<int>e[N];

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    return 0;
}
  • 图的bfs遍历
void bfs(int x){
    int head=0,tail=1;
    q[0]=x;vis[x]=1;
    while(head!=tail){
        int u=q[head];head++;
        for(int i=0;i<E[u].size();++i){
            int v=e[u][i];
            if(!vis[v]){
                vis[v]=1;
                dis[v]=dis[u]+1;
                q[tail++]=v;
            }
        }
    }
}
  • 图的dfs
void dfs(int x){
    vis[x]=1;
    for(int i=0;i<e[x].size();++i){
        if(!vis[x][i])
            dfs(e[x][i]);
    }
}

最短路

  • Floyd算法
    F[k, i, j] 表示经过k个点, i到j的最短路;
    F[k, i ,j] = F[k-1, i, k] + F[k-1, k , j]
    通过省略一维空间:
    F[i,j] = F[i, k] + F[k , j]
void floyd(){
    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]);
            }
        }
    }
}
  • SPFA
const int MAXN=1010;
const int INF=0x3f3f3f3f;
struct Edge{
    int v;
    int cost;
    Edge(int _v,int _cost):v(_v),cost(_cost){}
};
vector<Edge>E[MAXN];
void addedge(int u,int v,int w){
    E[u].push_back(E(v,w));
}
bool vis[MAXN];//判断在队列标注
int cnt[MAXN];//判断每个点入队列次数,以判断是否存在负环回路
int dist[MAXN];
bool SPFA(int start, int n){
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;++i)dist[i]=INF;
    queue<int>que;
    while(!que.empty())que.pop();
    que.push(start);
    vis[start]=true;
    dist[start]=0;
    memset(cnt,0,sizeof(cnt));
    cnt[start]=1;
    while(!que.empty){
        int u=que.front();
        que.pop();
        if(vis[u])continue;
        vis[u]=false;
        for(int i=0;i<E[u].size();++i){
            int v=E[u][i].v;
            if(dist[u]+E[u][i].cost<dist[v]){
                dist[v]=dist[u]+E[u][i].cost;
                if(!vis[v]){
                    vis[v]=true;
                    que.push(v);
                    if(++cnt[v]>n)return false;
                }
            }
        }
    }
    return true;
}
  • Dijkstra
const int INF=0x3f3f3f3f;
const int MAXN=100010;
struct qnode{
    int v;
    int c;
    qnode(int _v=0,int _c=0):v(_v),c(_c){}
    bool operator < (const qnode& r)const{
        return c>r.c;
    }
};
struct Edge{
    int v,cost;
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[MAXN];
bool vis[MAXN];
int dist[MAXN];
//点的编号从1开始
void Dijkstra(int n,int start){
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;++i)dis[i]=INF;
    priority_queue<qnode>que;
    while(!que.empty())que.pop();
    dis[start]=0;
    que.push(qnode(start,0));
    qnode tmp;
    while(!que.empty()){
        tmp=que.top();
        que.pop();
        int u=tmp.v;
        if(vis[u])continue;
        vis[u]=true;
        for(int i=0;i<E[u].size();++i){
            int v=E[tmp.v][i].v;
            int cost=E[u][i].cost;
            if(!vis[v]&&dist[u]+cost<dist[v]){
                dist[v[=dist[u]+cost;
                que.push(qnode(v,dist[v]));
            }
        }
    }
}
void addedge(int u,int v,int w){
    E[u].push_back(Edge(v,w));
}
  • CF475B 图的遍历
  • CF295B
  • CF1214D
  • NOIP 2009 最优贸易
n个点, m条边的有向图, 有些边是单向边, 有些边是双向边, 每个点有物品的买入和卖出价格. 现在从1号点走到n号点, 途中只能买一次物品,并在这之后卖出, 问最多能赚多少钱?  
  • CF666B World Tour
  • CF545E Paths and Trees

@Bryce1010
Copy link
Owner Author

图论知识及其应用

@Bryce1010
Copy link
Owner Author

树分治_黄哲威

@Bryce1010
Copy link
Owner Author

树链剖分_蒋一瑶

@Bryce1010
Copy link
Owner Author

树上倍增_黄哲威

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 9, 2020

图论_李煜东

图的遍历

  • 深度优先遍历

访问标记避免重复,时间戳

const int maxn=1000;
//采用链式前向星存储图
bool vis[maxn]={0};
void dfs(int s){
    vis[s]=true;
    //遍历当前点的所有邻接点
    for(int i=head[s];i!=-1;i=edge[i].next){
        dfs(edge[i].to);
    }
}
  • 广度优先遍历

循环队列, 优先队列
边权为01的图上双端队列

void bfs(int s){
    int que[maxn];
    int iq=0;
    que[id++]=s;
    int i,k;
    //循环至队列元素为空
    for(i=0;i<iq;++i){
        vis[que[i]]=ture;
        for(k=head[que[i]];k!=-1;k=edge[k].next){
            if(!vis[edge[k].to])
                que[iq++]=edge[k].to;
        }
    }
}
  • 拓扑排序

1)从有向图中选择一个入度为0的顶点并输出。
2)从网中删除该顶点,及其发出的全部边。
3)重复1)和2),直到所有的点都被输出。

//输入数据时统计每个点的入度,并存入indegree数组。
int queue[maxn];
int iq=0;
for(int i=1;i<=n;i++)
{
    if(indegree[i]==0)
    {
        queue[iq++]=i;
    }
}
for(int i=0;i<iq;i++)
{
    for(k=head[queue[i]];k!=-1;k=edge[k].next)
    {
        indegree[edge[k].to]--;
        if(indegree[edge[k].to]==0)
        {
            queue[iq++]=edge[k].to;
        }
    }
}

最短路问题

  • POJ 3613
  • POJ1734

Floyd算法

可以经过标号<=k的点中转时, 从i到j的最短路
F[k,i,j]=min{ F[k-1,i,k]+F[k-1,k,j]}
F[i,j]=min{F[k-1,i,k]+F[k-1,k,j]| O(n^3)
k->i->j

Dijkstra 算法

普通: O(n^2)
堆优化: O(NlogN)~O(MlogN)

Bellman-Ford算法

BF算法: O(N^2)
SPFA算法: 队列优化的BF算法; 稀疏图上O(kN), 稠密图上O(N^2)
可以应用于负权图

  • POJ 3463
  • POJ 3635
  • POJ 2449 第K短路

最小生成树

Kruskal

O(MlogN)
利用并查集, 起初每个点构成一个集合
所有边按照边权从小到大排序, 一次扫描
如果当前扫描得到边连接连个不同的集合, 合并

Prim

O(N^2)
以任一点为基准点, 将节点分为两组:

  • 在MST上到基准点的路径已经确定的点

  • 尚未在MST中与基准点相连的点
    不断从第2组中选择与第一组距离最近的点加入第1组

  • POJ 1639

  • BZOJ 1977 次小生成树

树上的倍增法

LCA

  • 倍增法
    静态, 在线, O(NlogN)-O(logN)

  • ST维护欧拉序
    静态, 在线, O(NlgN)-O(1)

  • Targan算法
    静态, 离线, O(N+M+Q)

  • Link-cut Tree (动态树)
    动态, 在线, O(N)- O(logN)

二分图

二分图匹配

最大匹配

增广路算法(匈牙利算法)

最小点覆盖

@Bryce1010
Copy link
Owner Author

图论知识及其应用

@Bryce1010
Copy link
Owner Author

图论专题生成树_唐文斌

@Bryce1010
Copy link
Owner Author

网络流_未知作者

@Bryce1010
Copy link
Owner Author

网络流_魏越闽

@Bryce1010
Copy link
Owner Author

网络流_周津浩&黄着威

@Bryce1010
Copy link
Owner Author

网络流建模 周尚彦

@Bryce1010
Copy link
Owner Author

Bryce1010 commented Apr 9, 2020

线性规划与网络流_曹钦翔

@Bryce1010
Copy link
Owner Author

2-SAT问题_未知作者

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant