染色和拓扑排序

邻接矩阵
判定二分图:一个无向图为二分图当且仅当图G中无奇数长度的回路。

#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 999;
int col[N], Map[N][N];

//0为白色,1为黑色

bool BFS(int s, int n)
{
    queue<int> p;
    p.push(s);
    col[s] = 1;  //将搜索起始点涂成黑色
    while(!p.empty())
    {
        int from = p.front();
        p.pop();
        for(int i = 1; i <= n; i++)
        {
            if(Map[from][i] && col[i] == -1)
                //如果从from到i的边存在(为邻接点) && i点未着色
            {
                p.push(i);          //将i点加入队列
                col[i] = !col[from];//将i点染成不同的颜色
            }

            if(Map[from][i] && col[from] == col[i])
    //如果从from到i的边存在(为邻接点) && i点和from点这一对邻接点颜色相同则不是二分图
                return false;
        }
    }
    return true;
    //搜索完s点和所有点的关系,并将邻接点着色,且邻接点未发现相同色则返回true
}



int main()
{
    int n, m, a, b;
    memset(col, -1, sizeof(col));
    cin >> n >> m;  //n 为有多少点,m为有多少边
    for(int i = 0; i < m; i++)
    {
        cin >> a >> b;
        Map[a][b] = Map[b][a] = 1;
    }
    bool flag = false;
    for(int i = 1; i <= n; i++)  //遍历并搜索各个连通分支
    {
        if(col[i] == -1 && !BFS(i, n))
        //每次找没有着色的点进行判断,如果从它开始BFS发现相同色邻接点则不是二分图
        {
            flag = true;
            break;
        }
    }
    if(flag)
        cout << "NO" <<endl;
    else
        cout << "YES" <<endl;
    return 0;

}

邻接表链式向前星

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+50;
const int M=1e5+50;
int n,m;
int u,v;

struct Edge{
    int v,next;
}edge[M];

int cnt,head[N];
int color[N];

void init(){//初始化
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addEdge(int u,int v){//建立无向无权图
    edge[cnt]=Edge{v,head[u]};
    head[u]=cnt++;
    edge[cnt]=Edge{u,head[v]};
    head[v]=cnt++;
}
bool dfs(int u,int c){//dfs遍历
    color[u]=c;
    for(int i=head[u];i!=-1;i=edge[i].next){//节点遍历
        int v=edge[i].v;
        if(color[v]==c){
            return false;
        }
        if(color[v]==0 && !dfs(v,-c)){
            return false;
        }
    }
    return true;
}
void solve(){
    memset(color,0,sizeof(color));
    for(int i=0;i<n;i++){//遍历节点
        if(color[i]==0){//当节点未染色进行二分判定
            if(!dfs(i,1)){
                printf("Wrong\n");
                return;
            }
        }
    }//如果都满足二分图的性质则此图为二分图
    printf("Correct\n");
}
int main(void){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        init();
        while(m--){
            scanf("%d%d",&u,&v);
            addEdge(u,v);
        }
        solve();
    }
    return 0;
}

拓扑排序
在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。
先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。
一直做改操作,直到所有的节点都被分离出来。如果最后不存在入度为0的节点,那就说明有环,
不存在拓扑排序,也就是很多题目的无解的情况。

用拓扑排序判断图中是否存在环

#include <stdio.h>
#include <queue>
#include<vector>
#include<stdlib.h>
using namespace std;
//拓扑排序中使用的对列和模拟链表的向量
vector<int> edge[501];//邻接链表
queue<int> Q;//保存入股为0的节点
int main(){
 //拓扑排序,判断一个有向图中是否存在环
    int inDegree[501];//统计每一个节点的入度;
    int n,m;
    while (scanf("%d%d",&n,&m)!=EOF) {//多组数据的测试
        if (m==0&&n==0) break;
        for (int i = 0; i<n; i++) {
            inDegree[i] = 0;//刚开始的节点入度均为0
            edge[i].clear();//清除前一组数据的残留
        }
        while(m--!=0){
            int a,b;//输入m组节点关系
            scanf("%d%d",&a,&b);
            inDegree[b]++;//出现了一条边指向b,所以入度增加1
            edge[a].push_back(b);//
        }
        while (Q.empty()==false) {
            Q.pop();//清除之前的数据
        }
        for(int i = 0;i<n;i++){
            if (inDegree[i]==0) {
                Q.push(i);
            }
        }
            int cnt = 0;
            while (Q.empty()==false) {//当队列中还有入度为0的点
                int newP = Q.front();
                Q.pop();//这里不需要保留拓扑排序的路径,因而不需要保存弹出来的值
                cnt++;
                for (int i = 0; i<edge[newP].size(); i++) {
                    inDegree[edge[newP][i]]--;//去除一条边后,将所指向的后继节点的如度减1
                    if (inDegree[edge[newP][i]]==0) {
                        Q.push(edge[newP][i]);
                    }
                }
            }
        if (cnt==n) {
            puts("YES");
        }else{
            puts("NO");
        }
    }
return 0;
}

正向 拓扑排序


queue<int>q;
    for(int i=0;i<n;i++)  //n  节点的总数
        if(in[i]==0) q.push(i);  //将入度为0的点入队列
    vector<int>ans;   //ans 为拓扑序列
    while(!q.empty())
    {
        int p=q.top(); q.pop(); // 选一个入度为0的点,出队列
        ans.push_back(p);
        for(int i=0;i<edge[p].size();i++)
        {
            int y=edge[p][i];
            in[y]--;
            if(in[y]==0)
                q.push(y);
        }
    }
    if(ans.size()==n)
    {
        for(int i=0;i<ans.size();i++)
            printf( "%d ",ans[i] );
        printf("\n");
    }
    else printf("No Answer!\n");   //  ans 中的长度与n不相等,就说明无拓扑序列
/********************************************************************************/
//有些拓扑排序要求字典序最小,那就把队列换成优先队列就好了

vector<int>v[MAXL+50];
int num[MAXL+50];
int n,m;
int ans[MAXL+50];
void toposort()
{
    priority_queue<int,vector<int>,greater<int> >q;//> >不要写在一起
    //当要求编号小的优先输出时用优先队列存储点
    for(int i=1;i<=n;i++)
        if(!num[i])
            q.push(i);
    int len=0;
    while(!q.empty())
    {
        int u=q.top();
        q.pop();
        ans[len++]=u;//记录排序
        for(int i=0;i<v[u].size();i++)
        {
            int t=v[u][i];
            num[t]--;
            if(!num[t])
                q.push(t);
        }
    }
    cout<<ans[0];
    for(int i=1;i<len;i++)
        cout<<" "<<ans[i];
    cout<<endl;
}
int main()
{
    while(cin>>n>>m)
    {
        memset(num,0);
        for(int i=1;i<=m;i++)
        {
            int x,y;
            cin>>x>>y;
            num[y]++;//入度
            v[x].push_back(y);//邻接表
        }
        toposort();
        for(int i=0;i<=n;i++)
            v[i].clear();//初始化
    }
}

逆向 拓扑排序

通过做题发现,对于要求编号小的靠前输出这类题需要反向建图

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
#define MAXL 100000
using namespace std;

vector<int>v[MAXL+50];
int num[MAXL+50];
int n,m;
int ans[MAXL+50];
void toposort()
{
    priority_queue<int,vector<int>,less<int> >q;
    //当要求编号大的优先输出时用优先队列存储点
    for(int i=1;i<=n;i++)
        if(!num[i])
            q.push(i);
    int len=0;
    while(!q.empty())
    {
        int u=q.top();
        q.pop();
        ans[len++]=u;//记录排序
        for(int i=0;i<v[u].size();i++)
        {
            int t=v[u][i];
            num[t]--;
            if(!num[t])
                q.push(t);
        }
    }
    cout<<ans[len-1];
    for(int i=len-2;i>=0;i--)//反向输出
        cout<<" "<<ans[i];
    cout<<endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        memset(num,0,sizeof(num));
        for(int i=1;i<=m;i++)
        {
            int x,y;
            cin>>x>>y;
            //反向建图
            num[x]++;//入度
            v[y].push_back(x);//邻接表
        }
        toposort();
        for(int i=0;i<=n;i++)
            v[i].clear();//初始化
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43870114/article/details/87661636