【算法】图论学习笔记与代码实现

这学期两门主课 算法设计与分析(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
****************************************************************/

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值