@myecho
2017-03-19T02:22:36.000000Z
字数 3854
阅读 800
图论
1.队列实现的最短路算法,参考算法竞赛入门手册
#include <utility>#include <functional>typedef pair<int, int> pii;priority_queue<pii, vector<pii>, greater<pii> >q; //默认是less的//这里使用了greater是因为pair类型有自己的排序规则,先比较第一维,相等时比较第二维,因此需要push(make_pair(d[i], i)) 另外还需要给出container的类型,当自定义排序规则的时候当然也可以自定义排序规则struct cmp {bool operator () (const pair<int,int> i, const pair<int,int> j) {return i.first < j.first;}};取出pair节点时需要判断节点是否被解决掉了,此时可利用u.first!=d[x]优化掉visited数组。bool done[MAXN];for (int i =0; i < n; i++) d[i] = (i == 0 ? 0 : INF);memset(done, 0, sizeof(done));q.push(make_pair(d[0], 0));while (!q.empty()) {pii u = q.top();q.pop();int x = u.second;if (done[x]) continue;done[x] = true;for (int e = first[x]; e != -1; e = next[e]) if (d[v[e]] > d[x] + w[e]) {d[v[e]] = d[x] + w[e];q.push(make_pair(d[v[e]]), v[e]);}}
其中关于next数据实际上是稀疏图的邻接表
first[u]保存结点u的第一条边的编号,next[e]表示编号为e的边的下一条边的编号
void read_graph() {
cin >> n >> m;
for (int i = 0; i < n; i++) first[i] = -1;
for (int e = 0; e < m; ++e) {
cin >> u >> v >> w;
next[e] = first[u];
first[u] = e;
}
}
2. SPFA->负权边求最短路
int spfa(int s){visited[]s=1;q.push(s);d[s]=0;while(!q.empty()){int u=q.front(),q.pop();visited[u]=0; //visited 标记此节点是否在队列中for (int i=1;i<=n;++i) {if (edges[u][i]!=0) {if (d[i]>d[u]+edges[u][i]) {d[i]=d[u]+egdes[u][i];//pre[i]=u;if (!visited[i]) {cnt[i]++;if (cnt[i] > n) {return true; //存在负环}visited[i] = 1;q.push(i);}}}}}}
int fa[1000]; //初始化fa[i]=iint find(int x) {int r = x;while(r != fa[r]){r = fa[r];}int i,j;i = x;while(i != r) { //路径压缩j=fa[i];fa[i]=r;i=j;}return r;}int join(int x,int y) {int r1=find(x);int r2=find(y); //注意这里非常容易出错,一定是两个树根的合并!if(r1!=r2){fa[r1]=r2;}return fa[r1];}
方法一: 用一个栈来保存入度为0的点较好
//初始化要存储邻接关系以及indegree[]数组首先遍历indegree数组,发现==0时入栈(入度为0的顶点入栈)while(!e.empty()) {int u = e.top();e.pop();indegree[u] = -1;//从邻接关系中得到所有的邻接边并处理--indegree[i],==0时也入栈}//遍历indegree数组查看是否元素全部为-1了方法二:**深搜的逆后序即可拓扑排序的顺序**前序:在深搜递归调用之前将顶点加入队列后序:在递归调用之后将顶点加入队列逆后序:在递归调用之后将顶点压入栈
class node{public:int u;int v;int cost;};bool compare(const node&i,const node&j){return i.cost<j.cost;}int krusal(int n){int num=0;for(int i=0;i<q.size();++i){int r1=find(q[i].u);int r2=find(q[i].v);if(r1!=r2){used[q[i].u][q[i].v]=1;//为了输出最小生成树,也可以直接储存下用到的边的标号即可fa[r1]=r2; //合并时有一个秩优化,即将较小的树合并到较大的树上边(http://noalgo.info/454.html)num++;}if (num == n - 1) return true;}return num == n -1;}int main(){sort(q.begin(),q.end(),compare);krusal();else //output}
7.Floyd算法
用于求任意两点之间的最短路
//初始化:d[i][i] = 0;初始化边的集合,其他d[i][k]为INF,其中INF设置为最短路径的上限即可int minCircle = INF;for (int k = 0; k < n; ++k) {for(int i = 0; i < k; i++)for(int j = 0; j < i; j++)minCircle = min(minCircle, d[i][j] + gra[i][k] + gra[k][j]); //添加的这一部分可用于求最小环for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];}
图的连通性
利用dfs和bfs在图上进行连通性的判断。关于图的存储,一种是存储邻接关系,以邻接表的形式,另外一种是存储相邻边,通过N*N的矩阵实现,两者都可以借助vector实现。
连通分量计算
无向图:通过dfs计算,即下图删掉按照逆后序即可
有向图:Kosaraju算法
按照逆后序进行深搜,同时标记一下分支的id
原理就是逆后序实际上是从入度为0的点开始的,是个source,从它开始遍历不会走出这个连通分量
void Kosaraju() {
for (int s : order.reversePost()) {
if (!marked[s]) {
dfs(G, s);count++;
}
}
}
void dfs() {
markd[v] = true;
id [v] = count;
for (int w : G.adj(v)) {
if (!markd[w]) dfs(G, w);
}
}
10. 有无环的判定
实际上在dfs的过程中就可以办到,都是查找反向边的过程
无向图:
从dfs(G, s, s)开始即可。
void dfs(Digraph G, int v, int pre) {
marked[v] = true;
for (int w : G.adj(v)) {
if (!marked[w]) dfs(G, w);
else if (w != pre) {
//有环
}
}
}
有向图:
boolean[]onstack;
void start() {
for (int v = 0; v < G.v(); v++)
if (!marked[v]) dfs(G,v);
}
void dfs(Digraph G, int v) {
onStack[v] = true;
marked[v] = true;
for (int w : G.adj(v)) {
if (this.hasCycle()) return;
else if (!marked[w]) dfs(G, w);
else if (onStack(w)) {
//有环,同时可以得到路径
}
onStack[v] = false;
}
}
注意事项:
1. 图论的题目要注意多重边 负环 成环等特殊情况 no loops指的是没有自环
2. 关掉同步 ios::sync_with_stdio(false);
3. spfa是比二维数组的算法要快的
4. 统一处理string与int输入->atoi(t.c_str())
5. 控制精度 #include cout<