HDU – 2544 模板题
第一种: Dijkstra算法
//用于求单源最短路径
void dij(int u) { //从u点开始找出距离所有的点最短距离. n 是点数.
Fill(vis,0);
for(int i = 1 ; i <= n ; i ++) near[i] = g[u][i];
vis[u]=1;
for(int i = 1 ; i <= n ; i ++) {
int minn=inf, v=-1;
for(int j = 1 ; j <= n ; j ++) {
if(!vis[j] && near[j] < minn)
v = j, minn = near[j];
}
if(v != -1){
vis[v] = 1;
for(int j = 1 ; j <= n ; j ++)
near[j] = min(near[j], g[v][j] + near[v]);
//就是通过当前我们选择了的这个点转折一下能否更短, 可以的话就更新它.
}
}
printf("%d\n", near[n]);
}
带堆优化的Dij, 注意它与spfa的区别, 一个是优先队列, 首先推出与当前点边长最小的, 推进的也是边的长度. 而spfa是一个普通队列, 推出推进的都是点的标号. 以一个点不会多次入队为优化条件! 所以分清楚! 但是前者不适用有负权值边, 而spfa是可以判负环(正环)的 ! 各有千秋
const int maxn = 1e4+5;
int n, m;
struct node {
int to, next, w;
bool operator < (const node& a) const {
return w > a.w;
}
} e[maxn<<1];
int cnt, head[maxn];
void add(int u, int v, int w) {
e[cnt] = node{v, head[u], w};
head[u] = cnt++;
}
void init() {
cnt = 0; Fill(head, -1);
}
bool vis[maxn];
int dis[maxn];
void dij(int st,int ed) {
priority_queue q;
Fill(dis,inf); Fill(vis,0);
dis[st] = 0;
q.push(node{st, 0, 0});
while (!q.empty()) {
node u = q.top();
q.pop();
if(vis[u.to]) continue;
vis[u.to] = 1;
for (int i = head[u.to]; ~i; i = e[i].next) {
int to = e[i].to;
if (dis[to] > dis[u.to] + e[i].w) {
dis[to] = dis[u.to] + e[i].w;
q.push(node{to, 0, dis[to]});
}
}
}
// cout << dis[ed] << endl;
}
注意: 该算法不适用于图中有负权值边. 比如这组数据(无向)
1 3 3
1 2 2
2 3 -2
那么输出应该是dis[2] = 2, dis[3] = 3. 显而易见是错的. 就是因为每次遍历时都是从起点可以向外拓展的边遍历的, 如果权值都为正, 这是正确的. 但是如果有负的边, 那么它可能会没有判到那条负权值边.
第二种 最暴力 的算法,floyd(适用于点数比较少的情况,允许有权值为负的边)(可以打印任意两点间的最短距离)(复杂度飞常高,不是一定要用,则尽量不要用)
const int maxn = 1e4+5;
int g[maxn][maxn]; // k的内外层就是决定了多次转折还是一次转折.
void floyd() {
for (int k = 1 ; k <= n ; k ++)
for (int i = 1 ; i <= n ; i ++)
for (int j = 1 ; j <= n ; j ++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
第三种,SPFA算法,这个算法是最好的,也可以适用于权值有负的边,时间复杂度也是最低的.(也是可以优化的+SLF,可以去网上搜搜模板)(求源点到其余点的最短距离)
附上有临接链表写的spfa, 写spfa都用这种方法.
这是水题 poj — 3013 题目在此的spfa写法.
(对于最短路中,dij要T的就一般用spfa了,即对于点和路比较多的那种用spfa,点比较少的用普通的dij和floyd就行.)
// 一般都是用来判环来用的. 在判的过程中求最短路.
判环模板题
const int maxn = 1e5+5;
int n, m;
struct node {
int to, next, w;
}e[maxn];
int cnt, head[maxn];
void add(int u, int v, int w) {
e[cnt] = (node){v, head[u], w};
head[u] = cnt++;
}
void init() {
cnt = 0; Fill(head, -1);
}
int dis[maxn];
int tim[maxn], vis[maxn];
bool spfa(int st) { // 这个判单独的联通块是没有任何问题的, 所以如果是非联通图, 那么就想我注释里这样写.
queue<int >q; q.push(st);
Fill(dis, inf); Fill(vis, 0); Fill(tim, 0);
// for (int i = 1 ; i <= n ; i ++) {
// q.push(i);
// dis[st] = 0; vis[st] = 1;
// } // 非联通图判环, 直接全部先推进队列
dis[st] = 0; vis[st] = 1; tim[st]++; // 联通图判环
while(!q.empty()){
int u = q.front();
q.pop(); vis[u]=0;
for(int i = head[u] ; ~i ; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[u] + e[i].w) { //注意看题目中0是否为负环, 是则加个=, 否则不加!
dis[to] = dis[u] + e[i].w;
if (++tim[to] > n) return true;
if(!vis[to]) {
vis[to] = 1;
q.push(to);
}
}
}
}
return false;
}