@myecho
2017-03-19T10:22:36.000000Z
字数 3854
阅读 674
图论
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]=i
int 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<