这学期两门主课 算法设计与分析(Algorithm and Analysis,ADA) 和 图论与算法(Graph Theory and Algorithms, GTA) 都涉及到很多经典图论问题以及相关代码实现,遂写下此文,以记录学习过程,随缘更新。
深度优先遍历(经典DFS):
从图中某一个节点出发,如果有未探索的邻居节点,则选择其中应该进行深入探索(其他节点暂时不管),每到一个新的节点则递归进行上述过程。当探索无法继续时(当前节点的所有邻居都在前面的探索过程中被标记过),则沿原路径回退。回退到某个节点时,若它还有其他的邻居节点没有探索,则继续深入探索。
使用白色,黑色,灰色记录每个节点的状态:
白色:表示一个节点尚未被遍历;
灰色:表示一个节点已经被遍历到,但是对于它的遍历尚未结束;即该节点还有若干邻居节点等待遍历,而当前算法正在递归处理其中一个邻居节点;
黑色:表示一个节点的所有邻居已经完成遍历,其自身的遍历也结束。
框架代码(包括封装和实现两部分):
具体实现(根据DFS遍历顺序简单输出一下各个节点的编号):
// DFS
#include <iostream>
#define N 10000
using namespace std;
int graph[N][N];
int color[N]; // 0 white, 1 grey, 2 black
int n, m;
void dfs(int v)
{
color[v] = 1;
for (int i = 1; i <= n; i++)
{
if (i != v && graph[v][i] == 1)
{
if (color[i] == 0)
{
dfs(i);
}
}
}
cout << v << " ";
color[v] = 2;
}
void my_dfs()
{
for (int i = 1; i <= n; i++)
{
if (color[i] == 0)
dfs(i);
}
cout << endl;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < m; i++)
{
int x, y;
cin >> x >> y;
graph[x][y] = 1;
}
my_dfs();
return 0;
}
/*
input:
7 13
1 2
1 3
1 6
2 3
2 4
4 1
4 3
5 3
5 7
6 1
6 3
7 4
7 5
output:
3 4 2 6 1 7 5
*/
广度优先遍历(BFS):
从图中某个节点出发,首先处理该节点指向邻居的所有边,然后将所有邻居放入一个调度器——队列(queue)中,接着处理该节点。下一个遍历的节点从当前被发现但未被处理的点中选出,即队列的首个节点,然后将其邻居节点依次放入队尾,处理该节点自身。重复以上过程,直到队列为空,所有节点均被处理完毕。
仍然以上面的图为例,输出BFS顺序遍历每个节点的值:
// BFS
#include <iostream>
#include <queue>
#define N 10000
using namespace std;
int graph[N][N];
int color[N]; // 0 white, 1 grey, 2 black
int parent[N];
int dis[N];
int n, m;
void bfs(int v)
{
queue<int> que;
color[v] = 1; // grey
dis[v] = 0; // 源点
que.push(v);
while(!que.empty())
{
int w = que.front();
que.pop();
for (int i = 1; i < n; i++)
{
if (i != w && graph[w][i] == 1)
{
if (color[i] == 0)
{
color[i] = 1; //grey
parent[i] = w;
dis[i] = dis[w] + 1;
que.push(i);
}
}
}
cout << w << " ";
color[w] = 2; // black
}
}
void my_bfs()
{
for (int i = 1; i <= n; i++)
{
color[i] = 0;
parent[i] = 0;
dis[i] = 0;
}
for (int i = 1; i <= n; i++)
{
if (color[i] == 0)
bfs(i);
}
cout << endl;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < m; i++)
{
int x, y;
cin >> x >> y;
graph[x][y] = 1;
}
my_bfs();
return 0;
}
/*
input:
7 13
1 2
1 3
1 6
2 3
2 4
4 1
4 3
5 3
5 7
6 1
6 3
7 4
7 5
output:
1 2 3 6 4 5 7
*/
网络中的最大流:
题目描述:
给定n个点,m条有向边,给定每条边的容量,求从点s到点t的最大流
输入:
第一行四个整数n,m,s,t。
接下来的m行,每行三个整数u,v,c,表示u到v,流量为c的一条边,
输出:
输出点s到点t的最大流。
样例输入 :
7 14 1 7 1 2 5 1 3 6 1 4 5 2 3 2 2 5 3 3 2 2 3 4 3 3 5 3 3 6 7 4 6 5 5 6 1 6 5 1 5 7 8 6 7 7
样例输出 :
14
提示:
1<=n<=100,1<=m<=5000,0<=c<=2147483647
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10001, E = 200001;
int n, m, s, t;
long long ans = 0;
long long cnt = 1, first[N], nxt[E], to[E], val[E];
inline void addE(int u, int v, long long w)
{
to[++cnt] = v;
val[cnt] = w;
nxt[cnt] = first[u];
first[u] = cnt;
}
int dep[N], q[N], l, r;
bool bfs()
{
memset(dep, 0, (n + 1) * sizeof(int));
q[l = r = 1] = s;
dep[s] = 1;
while (l <= r)
{
int u = q[l++];
for (int p = first[u]; p; p = nxt[p])
{
int v = to[p];
if (val[p] and !dep[v])
{
dep[v] = dep[u] + 1;
q[++r] = v;
}
}
}
return dep[t];
}
long long dfs(int u, long long in)
{
if (u == t)
return in;
long long out = 0;
for (int p = first[u]; p and in; p = nxt[p])
{
int v = to[p];
if (val[p] and dep[v] == dep[u] + 1)
{
long long res = dfs(v, min(val[p], in));
val[p] -= res;
val[p ^ 1] += res;
in -= res;
out += res;
}
}
if (out == 0)
dep[u] = 0;
return out;
}
int main()
{
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++)
{
int u, v;
long long w;
cin >> u >> v >> w;
addE(u, v, w);
addE(v, u, 0);
}
while (bfs())
ans += dfs(s, 1e18);
cout << ans << endl;
return 0;
}
/**************************************************************
Problem: 1006
User: 201830210
Language: C++
Result: 正确
Time:56 ms
Memory:0 kb
****************************************************************/
二分图中的最大匹配问题:
题目描述:
给定一个二分图,其左部点的个数为n,右部点的个数为m,边数为e,求其最大匹配的边数。
左部点从1至n编号,右部点从1至m编号。
输入:
输入的第一行是三个整数,分别代表n,m,e。
接下来输入e行,每行两个整数u,v,表示存在一条连接左部点u和右部点v的边。
输出:
输出一个整数,代表二分图最大匹配的边数。
样例输入:
4 2 7 3 1 1 2 3 2 1 1 4 2 4 1 1 1
样例输出:
2
提示:
1<= n,m <=100
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int cnt=2;
int alist[6000001];
struct data
{
int v;
int next;
int value;
}edge[6000001];
void add(int u,int v,int value)
{
edge[cnt].v=v;
edge[cnt].value=value;
edge[cnt].next=alist[u];
alist[u]=cnt++;
return ;
}
int h[1000001];
int q[1000001];
bool bfs()
{
int x,next;
memset(h,-1,sizeof(h));
int head=0,tail=1;
q[head]=1;
h[1]=0;
while(head<tail)
{
x=q[head++];
next=alist[x];
while(next)
{
int v=edge[next].v;
int value=edge[next].value;
if(value&&h[v]<0)
{
q[tail++]=v;
h[v]=h[x]+1;
}
next=edge[next].next;
}
}
if(h[n]==-1) return false;
return true;
}
int ans;
int dfs(int x,int y)
{
if(x==n) return y;
int next=alist[x];
int w,used=0;
while(next)
{
int v=edge[next].v;
int value=edge[next].value;
if(value&&h[v]==h[x]+1)
{
w=y-used;
w=dfs(v,min(w,value));
edge[next].value-=w;
edge[next^1].value+=w;
used+=w;
if(used==y) return y;
}
next=edge[next].next;
}
if(!used) h[x]=-1;
return used;
}
void dinic()
{
while(bfs()) ans+=dfs(1,0x7fffffff);
}
int n1,m1,e1;
int main()
{
scanf("%d%d%d",&n1,&m1,&e1);
n=n1+m1+2;
for(int i=1;i<=n1;i++)
{
add(1,i+1,1);
add(i+1,1,1);
}
for(int i=1;i<=e1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
if(u<=n1&&v<=m1)
add(u+1,v+n1+1,1),
add(v+n1+1,u+1,1);
}
for(int i=1;i<=m1;i++)
{
add(i+n1+1,n,1);
add(n,i+n1+1,1);
}
dinic();
printf("%d",ans);
return 0;
}
/**************************************************************
Problem: 1005
User: 201830210
Language: C++
Result: 正确
Time:9 ms
Memory:0 kb
****************************************************************/