邻接矩阵
判定二分图:一个无向图为二分图当且仅当图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();//初始化
}
}