图论 [未完成 待续~待修改]

来源:互联网 发布:翅片式换热器计算软件 编辑:程序博客网 时间:2024/05/20 21:19

图的表示方法

矩阵

二维数组 map[i][j] 表示i到j的边 值为边得大小 ,定义一个值代表不存在这条边 一般为0
空间复杂度O(n2)

vector

vectorG[N];
G[i][j] 表示有一条i->j的有向边

void add(int u,int v){    G[u].push_back(v);}

空间复杂度O(e)

前向星

struct edge{    int to,next,w;}G[N<<1];int head[N],tot;void add(int u,int v,int w){    G[tot].w=w,G[tot].to=v,G[tot].next=head[u],head[u]=tot++;}

空间复杂度O(e)

最短路

floyd

不解释

dijkstra

vector<pair<int ,int > >G[N];int d[N];int main(){    while(~scanf("%d%d",&m,&n)){        for(int i=1;i<=n;i++)d[i]=2e9+7,G[i].clear();        d[n]=0;        for(int i=1,u,v,w;i<=m;i++){            scanf("%d%d%d",&u,&v,&w);            G[u].push_back(make_pair(v,w));            G[v].push_back(make_pair(u,w));        }        priority_queue<pair<int ,int> >q;        q.push(make_pair(-d[n],n));        while(!q.empty()){            int u =q.top().second;q.pop();            for(int i=0,v;i<G[u].size();i++){                v=G[u][i].first;                if(d[v]>d[u]+G[u][i].second){                    d[v]=d[u]+G[u][i].second;                    q.push(make_pair(-d[v],v));                }            }        }        if(d[n]==2e9+7) puts("-1");        else            printf("%d\n",d[1]);    }    return 0;}

SPFA

vector<pair<int ,int > >G[N];int d[N],inq[N];int main(){    while(~scanf("%d%d",&m,&n)){        for(int i=1;i<=n;i++)d[i]=2e9+7,inq[i]=0,G[i].clear();        for(int i=1,u,v,w;i<=m;i++){            scanf("%d%d%d",&u,&v,&w);            G[u].push_back(make_pair(v,w));            G[v].push_back(make_pair(u,w));        }        queue<int >q;        q.push(n);d[n]=0,inq[n]=1;        while(!q.empty()){            int u =q.front();q.pop();            inq[u]=0;            for(int i=0,v;i<G[u].size();i++){                v=G[u][i].first;                if(d[v]>d[u]+G[u][i].second){                    d[v]=d[u]+G[u][i].second;                    if(inq[v]==1) continue;                    inq[v]=0;                    q.push(v);                }            }        }        if(d[n]==2e9+7) puts("-1");        else            printf("%d\n",d[1]);    }    return 0;}

二分图匹配

算法介绍
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大匹配数 = 最大独立数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

匈牙利算法求解二分图匹配问题

int match[2000];int vis[2000];vector<int >mmp[2000];int find(int u){    for(int i=0;i<mmp[u].size();i++){        int v=mmp[u][i];        if(vis[v]==0){            vis[v]=1;            if(match[v]==-1||find(match[v])){                match[v]=u;                return 1;            }        }    }    return 0;}void init(){    memset(match,-1,sizeof(match));    for(int i=1;i<=n;i++) mmp[i].clear();}void add(int u,int v){    mmp[u].push_back(v);    //mmp[v].push_back(u); //无向图才需要}int maxMATCH(){    int ans = 0;  //ans 是最大匹配数,    for(int i=1;i<=n;i++){        memset(vis,0,sizeof(vis));        if(find(i)) ans++;    }    //ans/=2;   //对于无向图的话 需要ans/=2;    return ans ;}bool perfectMATCH(){    if(maxMATCH()==n) return true;     else              return false;}

2-SAT

强连通分支及其应用(2-SAT)总结

2-SAT

强联通分量一个很重要的用途就是解布尔方程可满足性问题(SAT)。需要学习这一部分知识我们需要一点布尔代数的知识。

下文中我们约定(^表示交v表示并)

例如:(a v b v …)^(c v d v …)^…

这样的我们叫做合取范式。其中(a v b v …)这样的叫做子句。类似a,b…叫做文字。

我们把合取范式中一个子句中包含文字不超过两个的问题成为2-SAT问题。在SAT问题中只有这一类我们可以用线性时间内得出答案。

最常规的2-SAT题目分为大致两种,一种是让你判断有没有解,另一种是让你输出一组解。针对这两种给出模版。

在这之前先来介绍一下2-SAT题目的大致解题步骤:

对于2-SAT问题我们需要构建一张有向图每个文字拆为两个节点 例如 a 变为 a, !a

首先我们从题目中总结出来的都是一些比较杂乱的逻辑表达式,不过一般都是两两之间的关系,我们需要做的第一步是化简成用^连接。然后对于每个子句建边。

建边的规则是这样的 a -> b那么在有向图中建一条a到b的边

我们可能得到的子句有:

a v b 我们可以化简 !a->b ^ !b->a

a -> b 直接连边

a 转化为!a -> a

其中每个文字及其的非对应相应的结点,若是出现在文字前有非的关系例如 !a v b 那么变通一下 就化成 a -> b ^ !b -> !a就可以了。

到这里我们要做的事(建图)就完成了,接下来交给模版,我们来看一下模版做了什么:

首先我们对建完的有向图求强连通分支,若是出现有一个逻辑变量和他的反在同一个联通分之内就无解,否则有解。

若a所在的强连通分支的拓扑序在!a之后a为真,否则为反。怎么样很简单吧。

vector<int> G[N];vector<int> G2[N];vector<int> S;int vis[N] ;  //标记数组int sccno[N]; //记录数组 sccno[i] 记录  i属于哪个联通分支int scc_cnt;  int n;void dfs1(int u){    if(vis[u]) return ;    vis[u] = true;    for(int i = 0; i < G[u].size(); i++){        dfs1(G[u][i]);    }    S.push_back(u);}void dfs2(int u){    if(sccno[u]) return;    sccno[u] = scc_cnt;    for(int i = 0; i < G2[u].size(); i++){        dfs2(G2[u][i]);    }}void find_scc(int n){    scc_cnt = 0;    S.clear();    memset(vis , 0 , sizeof(vis));    memset(sccno , 0 , sizeof(sccno));    for(int i = 0; i < n; i++) dfs1(i);    for(int i = n - 1; i >= 0; i--) if(!sccno[S[i]]){        scc_cnt++;        dfs2(S[i]);    }}void AddEdge(int u , int v) {    G[u].push_back(v);    G2[v].push_back(u);}int main(){    /***    输入数据    */    /***    加边  边未两者不能匹配的      example :    {u,v}  当选择u的时候一定不能选择v ,需要选择v';    */    find_scc(2 * n);  //注意x2    for(int i = 0; i < n; i++){        if(sccno[i] == sccno[i + n]){            //如果有x与x'同时被取或者未取,则匹配失败            puts("NO");            return 0;        }    }    puts("YES");    for(int i = 0; i < n; i++){        if(sccno[i] > sccno[i + n]){            //相关 i;        }        else {            //相关 i';        }    }    return 0;}/********************************************************//****************************************     2-SAT kosaraju算法             By 小豪                *****************************************/const int LEN = 200000+10;vector<int> Map[LEN], rMap[LEN], vs;int n, m, vis[LEN], sccn[LEN];void dfs(int v){    vis[v] = 1;    for(int i=0; i<Map[v].size(); i++)        if(!vis[Map[v][i]]) dfs(Map[v][i]);    vs.PB(v);}void rdfs(int v, int f){    vis[v] = 1;    sccn[v] = f;    for(int i=0; i<rMap[v].size(); i++)        if(!vis[rMap[v][i]]) rdfs(rMap[v][i], f);}int scc(){     memset(vis, 0, sizeof vis);     vs.clear();     for(int i=0; i<2*n; i++) if(!vis[i]) dfs(i);     memset(vis, 0, sizeof vis);     int k = 0;     for(int i = vs.size()-1; i>=0; i--) if(!vis[vs[i]]) rdfs(vs[i], k++);     return k;}void addedge(int a, int b){    Map[a].PB(b);    rMap[b].PB(a);}void solve(){    scc();    for(int i=0; i<2*n; i+=2)        if(sccn[i] == sccn[i+1]){                //printf("No solution.\n");             //无解            return ;        }    for(int i=0; i<n; i++){        if(sccn[i*2] > sccn[i*2+1]) printf("Yes\n");        else printf("No\n");    }}
0 0
原创粉丝点击