图的邻接表的数组实现及其应用
来源:互联网 发布:淘宝舞蹈服 编辑:程序博客网 时间:2024/05/29 19:45
一、基础
对于有向图来说,我们把每条边的起始点(from)称为尾节点,终止点称为头结点。
我们可以把每条边按如下定义:
typedef struct{ int to; int w; int next;}Edge; Edge e[MAX]; //MAX为边的数量。e为结构体数组,用于存储边的信息int pre[MAX]; //pre数组记录的当前边的序号,pre[i]表示尾节点是 i 的边的序号是pre[i]//初始化memset(pre,-1,sizeof(pre)); //pre数组初始化为-1是正确的,原因后面阐述//输入,循环scanf("%d %d %d",&from,&to,&w1);e[i].to = to; e[i].w = w1; e[i].next = pre[from]; pre[from] = i;i++;
下面举例说明其正确性:
输入边的信息为(输入边的次序即为边的序号):
边序号 from to
0 1 2
1 1 3
2 2 4
3 3 4
4 1 4
则经过上面的程序之后,所得结果为(省略掉 to 和 w 的信息)
初值i=0
e[0].next= pre[1] = -1 , pre[1] = 0 , i = 1
e[1].next= pre[1] = 0 , pre[1] = 1 , i = 2
e[2].next= pre[2] = -1 , pre[2] = 2 , i = 3
e[3].next= pre[3] = -1 , pre[3] = 3 , i = 4
e[4].next= pre[1] = 1 , pre[1] = 4 , i = 5
我们可以看出 e[i].next 记录上一条从 i 出发的边的序号(想想它是如何做到的),所以如果只有一条从i 出发的边,那么e[i].next=pre[ i ] = -1,因为 i 没有上一条边,这就是pre数组初始化为-1的原因。
由此,我们可以如下操作一个顶点的所有邻接顶点:
/* now为尾结点序号,i为now所发出的边序号,adj为 头结点序号,w为now-->adj这条边的权值 */for(i = pre[now]; i != -1; i = e[i].next){ int adj = e[i].to ; int w = e[i].w ; //do something...}
参考资料:http://www.cnblogs.com/g0feng/archive/2012/09/18/2690913.html
二、应用 POJ 3249 Test for Job (http://poj.org/problem?id=3249)
#include <iostream>#include <cstdlib>#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <queue>using namespace std;typedef long long LL;const int MAXN = 100010;const int MAXM = 1000010; //最多边const int INF = 0x3f3f3f3f;struct Edge{int v;LL w;int next;}e[MAXM]; int n, m;int count1, tot;int count2;LL d[MAXN], w[MAXN];int pre[MAXN]; //以顶点数为参考,而非边数int topo[MAXN];int ind[MAXN], outd[MAXN]; //入度和出度int save[MAXN];void init(){ tot = 0;count1 = 0;count2 = 0;memset(pre, -1, sizeof(pre));memset(ind, 0, sizeof(ind));memset(outd, 0, sizeof(outd));}void read_graph(int u, int v, LL w){ e[count1].v = v; e[count1].w = w; e[count1].next = pre[u]; pre[u] = count1++;}void toposort(){queue<int> q;for(int i = 0; i <= n; i++) { if(!ind[i]) q.push(i); }while(!q.empty()){int x = q.pop();topo[tot++] = x; //出栈次序即为拓扑排序的次序for(int i = pre[x]; i!= -1; i = e[i].next) //找出 x 所有的邻接顶点{int v = e[i].v; //邻接顶点if(--ind[v] == 0) //入度减1{ q.push(v);}}}}void DAGLongestPath(int src) //DAG有向无环图的最长路径{for(int i = 0; i <= n; i++) //新增0节点,0~n 共 n+1 个节点 d[i] = ((i == src)? 0:-INF); //源节点的d[0]=0,其他皆初始化为负无穷大for(int u = 0 ; u < tot; u++) //对于每一个顶点{int x = topo[u]; //拓扑排序好的顶点次序for(int i = pre[x]; i!= -1; i = e[i].next){int v = e[i].v, w = e[i].w;if(d[v] < d[x] + w){d[v] = d[x] + w;}}}}void read_case(){init( );for(int i = 1; i <= n; i++){scanf("%lld", &w[i]);}while(m--){int u, v;scanf("%d%d", &u, &v);read_graph(u, v, w[v]); //点权转换为边权(w[v]) outd[u]++;ind[v]++;}for(int i = 1; i <= n; i++){if(!outd[i]) //储存出度为0的点,即终点{save[count2++] = i; //i为出度为0的点的编号}if(!ind[i]){read_graph(0, i, w[i]); //没有入边的点需要添加一个节点0,并连接一条有向边 (增加一个超级源点 0 )ind[i]++;}}}void solve(){read_case();toposort();DAGLongestPath(0);LL ans = -INF;for(int i = 0; i < count2; i++) //枚举最大值(出度为0(出口)的顶点的个数),因为有可能不止一个出口{ ans = max(ans, d[save[i]]); //save[i]表示出度为0的顶点的编号,这样的顶点共有count2 个!这里很重要,要想清楚(这里的i不同于save[count2++] = i;的i)!!}printf("%lld\n", ans);}int main(){while(~scanf("%d%d", &n, &m)){solve();}return 0;}
改变后的图
参考资料:http://blog.csdn.net/wall_f/article/details/8205246
- 图的邻接表的数组实现及其应用
- 图的邻接表存储及其实现
- 邻接表的数组实现
- 邻接表的数组实现
- 邻接表的数组实现
- 邻接表-数组的实现
- 图的邻接表的实现(链表+数组)
- 巧妙的邻接表(数组实现)
- 巧妙的邻接表(数组实现)
- 巧妙的邻接表(数组实现)
- 详解--邻接表的数组实现【转载】
- 巧妙的邻接表(数组实现)
- 巧妙的邻接表(数组实现)
- Java实现基于邻接表的图的几个应用
- 图论之邻接表的数组实现
- 稀疏图的邻接表(数组实现)
- 图的邻接表表示及其深度优先搜索实现
- 图的邻接表实现
- Lake Counting
- 网上订餐系统饿了么、百度、美团外卖,订餐网站外卖
- 火力篮球游戏源码完整版--采用标准的游戏开发文档
- java中import两种导入类型比较
- 程序员准备面试时常犯的10个错误
- 图的邻接表的数组实现及其应用
- Android笔记-ListView总结(多选框ListViiew,动态加载,多线程更新ListView中的进度条)
- bugfix:录音的时候,webview不能播放视频
- fastjson 序列化部分源码解析②
- 科普:什么样的电子合同才合法有效?
- windows配置环境变量
- 关于c中可变参数的一点学习
- Android SharedPreferences用法及程序共享机制
- 大型网站架构改进历程:存储的瓶颈(1)