最短路问题

记录
22:22 2024-1-31

1.最短路

相关知识

Dijkstra:处理没有负边的情况。

Bellman-Ford:可以处理边是负数的情况。不能计算图中有负圈的情况,可以用来判断是否存在负圈。

SPFA:队列优化的 Bellman-Ford 算法

Floyd-Warshall:可以处理边是负数的情况。

1.单源最短路

m指边数 n指节点个数

1.Dijkstra

O(n^2)

点击查看代码
const int INF = 0x3f3f3f3f;

int graph[MAX_V][MAX_V];

int d[MAX_V];      //距离
bool used[MAX_V];  //是否使用过
int V;  //节点个数

void dijkstra(int s) {
    fill(d, d + V, INF);
    fill(used, used + V, false);

    d[s] = 0;
    while (true) { //每个节点最多访问一次 所以最多循环V次
        int v = -1;

        //查找最小的节点
        for(int u = 0; u < V; u++) {
            if(!used[u] && (v == -1 || d[u] < d[v])) v = u;
        }

        if(v == -1) break;
        used[v] = true;

        for(int u = 0; u < V; u++) {
            d[u] = min(d[u], d[v] + graph[v][u]);
        }
    }   
}

O(mlogn)

这个优化是利用最小堆来获得目前的最短路径
利用vector

点击查看代码
struct edge{int to, cost;};
typedef pair<int, int> P;  //first 最短距离 second顶点编号

int V;
vector<edge> G[MAX_V];
int d[MAX_V];

void dijkstra(int s) {
    //堆按照first从小到大排序
    priority_queue<P, vector<P>, greater<P> > que;

    fill(d, d + V, INF);
    d[s] = 0;
    que.push(P(0, s));
    while (!que.empty()) {
        P p = que.top(); que.pop();
        int v = p.second;
        if(d[v] < p.first) continue;
        for(int i = 0; i < G[v].size(); i++) {
            edge e = G[v][i];
            if(d[e.to] > d[v] + e.cost) {
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to], e.to));
            }
        }
    }
    
}

利用邻接表

点击查看代码
const int N = 100010, M = 1000010;
int head[N], ver[M], edge[M], Next[M], d[N];
bool v[N];
int n, m, tot;
// 大根堆(优先队列),pair的第二维为节点编号
// pair的第一维为dist的相反数(利用相反数变成小根堆,参见0x71节)
priority_queue< pair<int, int> > q;

void add(int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}

void dijkstra() {
	memset(d, 0x3f, sizeof(d)); // dist数组
	memset(v, 0, sizeof(v)); // 节点标记
	d[1] = 0;
	q.push(make_pair(0, 1));
	while (q.size()) {
		// 取出堆顶
		int x = q.top().second; q.pop();
		if (v[x]) continue; //每个节点只会访问一次
		v[x] = 1;
		// 扫描所有出边
		for (int i = head[x]; i; i = Next[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				// 更新,把新的二元组插入堆
				d[y] = d[x] + z;
				q.push(make_pair(-d[y], y));
			}
		}
	}
}

2.Bellman-Ford

点击查看代码
const int INF = 0x3f3f3f3f;
struct edge{int from, to, cost;};

edge es[MAX_E]; //边
int d[MAX_V];  //最短距离
int V, E;

void shortest_path(int s) {
    for(int i = 0; i < V; i++) d[i] = INF;
    d[s] = 0;

    while (true) {
        bool update = false;
        for(int i = 0; i < E; i++) {
            edge e = es[i];
            if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost) {
                d[e.to] = d[e.from] + e.cost;
                update = true;
            }
        }
        if(!update) break;
    }
    
}

判断是否存在负圈

点击查看代码
const int INF = 0x3f3f3f3f;
struct edge{int from, to, cost;};

edge es[MAX_E]; //边
int d[MAX_V];  //最短距离
int V, E;

bool find_negative_loop() {
    memset(d, 0, sizeof(d));

    for(int i = 0; i < V; i++) {
        for(int j = 0; j < E; j++) {
            edge e = es[j];
            if(d[e.to] > d[e.from] + e.cost) {
                d[e.to] = d[e.from] + e.cost;

                //存在负圈
                if(i == V - 1) return true;
            }

        }
    }
    return false;
}

3.SPFA

点击查看代码
const int N = 100010, M = 1000010;
int head[N], ver[M], edge[M], Next[M], d[N];
int n, m, tot;
queue<int> q;
bool v[N];

void add(int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}

void spfa() {
	memset(d, 0x3f, sizeof(d)); // dist数组
	memset(v, 0, sizeof(v)); // 是否在队列中
	d[1] = 0; v[1] = 1;
	q.push(1);
	while (q.size()) {
		// 取出队头
		int x = q.front(); q.pop();
		v[x] = 0;
		// 扫描所有出边
		for (int i = head[x]; i; i = Next[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				// 更新,把新的二元组插入堆
				d[y] = d[x] + z;
				if (!v[y]) q.push(y), v[y] = 1;
			}
		}
	}
}

2.多源最短路

1.Floyd-Warshall

点击查看代码
int d[MAX_V][MAX_V];  //d[u][v]表示边e=(u,v)的权值 不存在时设为INF d[i][i] = 0;
int V;

void warshall_floyd() {
    for(int k = 0; k < V; k++) {
        for(int i = 0; i < V; i++) {
            for(int j = 0; j < V; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
        }
    }
}

传递闭包问题

若 a X b 且 b X c则 a X c

点击查看代码
bool d[310][310];
int n, m;

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) d[i][i] = 1;
	for (int i = 1; i <= m; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		d[x][y] = d[y][x] = 1;
	}
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				d[i][j] |= d[i][k] & d[k][j];
}

题目记录

POJ2130

posted @ 2024-01-31 22:23  57one  阅读(5)  评论(0编辑  收藏  举报