[集训笔记3rd]dijkstra算法专项 POJ1502 POJ3037

d i j k s t r a dijkstra dijkstra算法求单源非负权的最短路径

d i j k s t r a dijkstra dijkstra本质上的思想是贪心,它只适用于不含负权边的图.
把点分成两类,一类是已经确定最短路径的点,称为"白点",另一类是未确定最短路径的点,称为"蓝点"
d i j k s t r a dijkstra dijkstra的流程如下:

  1. 初始化dis[start]=0,其余节点的dis值为无穷大.
  2. 找一个dis值最小的蓝点x,把节点x变成白点.
  3. 遍历x的所有出边(x,y,z),若dis[y]>dis[x]+z,则令dis[y]=dis[x]+z.
  4. 重复2,3两步,直到所有点都成为白点
    时间复杂度为 O ( n 2 ) O\left(n^2\right) O(n2)

智商不够用啊,硬是没看懂模版。

模版题

Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before Farmer John wakes her for the morning milking. Bessie needs her beauty sleep, so she wants to get back as quickly as possible.
Farmer John’s field has N (2 <= N <= 1000) landmarks in it, uniquely numbered 1…N. Landmark 1 is the barn; the apple tree grove in which Bessie stands all day is landmark N. Cows travel in the field using T (1 <= T <= 2000) bidirectional cow-trails of various lengths between the landmarks. Bessie is not confident of her navigation ability, so she always stays on a trail from its start to its end once she starts it.
Given the trails between the landmarks, determine the minimum distance Bessie must walk to get back to the barn. It is guaranteed that some such route exists.

I n p u t Input Input

Line 1: Two integers: T and N
Lines 2…T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the trail travels. The third integer is the length of the trail, range 1…100.

O u t p u t Output Output

Line 1: A single integer, the minimum distance that Bessie must travel to get from landmark N to landmark 1.

S a m p l e I n p u t Sample Input SampleInput

5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100

S a m p l e O u t p u t Sample Output SampleOutput

90

题意:就是把模版敲上。(然后wa了11发

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
#define ll long long
using namespace std;

const int N = 100010, M = 1000010;
int head[N], ver[M], Next[M];
ll edge[M],d[N];
bool v[N];
int n, m, tot;
priority_queue<pair<ll, ll> > q;

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

void dijkstra() {
    d[1] = 1;
    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[y] < d[x] + z) continue;
            d[y] = d[x] + z;
            q.push(make_pair(-d[y], y));

        }
    }
    return;
}

int main() {
    while (cin >> m >> n) {
        for (int i = 0; i < m; i++) {

            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z);
            add(y, x, z);
        }
        dijkstra();
        printf("%d\n", d[n] - 1);
    }
}

敲了一波模版感觉没怎么理解,就是那种你自己知道应该怎么做,但是无法教会程序怎么做,无法用程序实现,很难受的感觉。我觉得是相关知识没跟上。。。

学习一下模板里除了此算法还用到了哪些知识
建图方式:邻接表

邻接表可以看成“带有索引数组的多个数据链表”构成的结构集合,在这样的结构中存储的数据被分成了若干类,每一类的数据构成一个链表。每一类还有个一个代表元素,成为该类对应链表的“表头”。所有“表头”构成一个表头数组作为一个可以随机访问的索引,从而可以通过表头数组定位到某一类数据对应的链表。这种结构被称为“邻接表”结构。
应用于图中的表现为:
在一个具有 n n n个点 m m m条边的有向图结构中,我们可以把每条边所属的“类别”定义为该边的起点标号。这样所有边被分为 n n n类,其中第 x x x类就由“从 x x x出发的所有边”构成。通过表头 h e a d [ x ] head[x] head[x],我们很容易定位到第 x x x类对应的链表,从而访问从点 x x x出发的所有边。

截自《算法竞赛进阶指南》李煜东

上图是在邻接表中插入一张5个点、6条边的有向图之后的状态。这6条边按照插入点顺序依次是 ( 1 , 2 ) ( 2 , 3 ) ( 2 , 5 ) ( 3 , 5 ) ( 5 , 4 ) ( 5 , 1 ) (1,2)(2,3)(2,5)(3,5)(5,4)(5,1) (1,2)(2,3)(2,5)(3,5)(5,4)(5,1)。展示了采用“数组模拟链表”的实现方式是,内部数据的实际存储方式。 h e a d head head n e x t next next数组中保存的是“ v e r ver ver数组的下标“,相当于指针,其中0表示指向空。 v e r ver ver数组存储的是每条边的终点,是真实的图数据。

//加入有向边(x,y)
void add(int x, int y, int z){
	ver[++tot] = y, edge[tot] = z;//tot是给边赋予一个序号,然后ver记录了边的终点,edge记录了边权
	next[tot] = head[x]//代表tot这条边的下一条边是上一次更新的从x点出发的第一条边
	head[x] = tot;//然后tot这一条边篡位取代了原来的第一条边,变成了由那个点出发的新的首出度
}

//访问x出发的所有边
for (int i = head[x]; i; i = head[i]){
	int y = ver[i], z = edge[i];
	//找到了一条有向边(x,y)它的边序号就是i,边权就是z
}
//感谢一下被我一下午的沙雕问题逼疯的KAW学长,总算搞明白了
void add(int x, int y, int z){
	ver[++tot] = y, edge[tot] = z, next[tot] = head[x], head[x] = tot;
}
for (int i = head[x]; i; i = head[i]){
	int y = ver[i], z = edge[i];
}
//无注解版

建图还可以用邻接矩阵和vector, 有可能会被卡数据,顺带提一句kruskal用邻接表好像不太方便。

优先队列

贴一下手写代码,在本专项就用STL好了,因为要用小顶堆。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int SIZE = 20;
template <typename T>
class priority_queue{
    T heap[SIZE];
    int n;
public:
    priority_queue(){
        n = 0;
    }
    void up(int p) {
        while (p > 1) {
            if (heap[p] > heap[p / 2]) {
                swap(heap[p], heap[p / 2]);
                p >>= 1;
            } else break;
        }
    }
    void push(int val) {
        heap[++n] = val;
        up(n);
    }
    int top() {
        return heap[1];
    }
    void down(int p) {
        int s = p * 2;
        while (s <= n) {
            if (s < n && heap[s] < heap[s + 1]) s++;
            if (heap[s] > heap[p]) {
                swap(heap[s], heap[p]);
                p = s;
                s = p * 2;
            } else break;
        }
    }
    void pop() {
        heap[1] = heap[n--];
        down(1);
    }
    void Remove(int k){
        heap[k] = heap[n--];
        up(k), down(k);
    }
    bool empty(){
        return n==0;
    }
};
int main() {
    priority_queue<int> yy;
    int a[] = {14, 19, 5, 18, 17, 8, 1, 3, 10, 6, 2, 25};
    for(int i = 0; i < 12; i++){
        yy.push(a[i]);
    }
    yy.Remove(4);
    while(!yy.empty()){
        cout << yy.top() << endl;
        yy.pop();
    }
    return 0;
}

POJ 1502

传送门
题意:给一个三角矩阵, 其中 A ( i , j ) A(i, j) A(i,j)表示 i i i j j j j j j i i i的边权,输出各点到点 1 1 1最短路径的最大值。

c o d e code code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>

using namespace std;
const int maxn = 1000050;
int ver[maxn], edge[maxn], Next[maxn], head[maxn], d[maxn];
bool v[maxn];
int tot;
priority_queue<pair<int, int> > q;

int read(string str) {
    int ans = 0;
    for (int i = 0; i < str.size(); i++) {
        ans = (ans << 3) + (ans << 1) + str[i] - '0';
    }
    return ans;
}

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));
    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));
            }
        }
    }
}

int main() {
    int n;
    string s;
    cin >> n;
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j < i; j++) {
            cin >> s;
            if (s == "x" || s == "X") continue;
            add(i, j, read(s));
            add(j, i, read(s));
        }
    }
    dijkstra();
    int ans = 0;
    for (int i = 2; i <= n; i++) {
        ans  = max(ans, d[i]);
    }
    //cout << d[n];
    cout << ans << endl;
    return 0;
}

这个题需要注意的是怎么处理输入,还有最后要输入一个换行符才行,不然会wa,其余的就是模板。

S k i i n g Skiing Skiing

传送门:poj3037
设海拔为H,由题意求从 ( 1 , 1 ) (1,1) (1,1) ( R , C ) (R,C) (R,C)的最短时间,所以时间 t t t就是边权,题意有解释从 A i A_{i} Ai A j A_{j} Aj花费的时间 T i j = 1 v i T_{ij}=\frac{1}{v_{i}} Tij=vi1, 而速度 V b = V a ∗ 2 H a − H b V_{b} = V_{a} * 2^{H_{a}-H_{b}} Vb=Va2HaHb
但如果细细思考的话,就会注意到当前速度只取决于初速度,初海拔,与当前高度:
比如说:
V a = V 0 ∗ 2 H 0 − H a V_{a} = V_{0} * 2^{{H_{0}-H_{a}}} Va=V02H0Ha
V b = V a ∗ 2 H a − H b = V 0 ∗ 2 H 0 − H a ∗ 2 H a − H b = V 0 ∗ 2 H 0 − H b V_{b} = V_{a} * 2^{H_{a}-H_{b}} = V_{0} * 2^{{H_{0}-H_{a}}} * 2^{H_{a}-H_{b}} = V_{0} * 2^{H_{0}-H_{b}} Vb=Va2HaHb=V02H0Ha2HaHb=V02H0Hb
于是我们得到以 A i A_{i} Ai 为起点的所有出边的边权值 W i = 1 V 0 ∗ 2 H 0 − H i W_{i} = \frac{1}{V_{0} * 2^{H_{0}-H_{i}}} Wi=V02H0Hi1
注意这个题疯狂卡double,边权的类型,输入初速度的类型都是double, 边权的最大值至少要1.0e12
而且蜜汁输出,如果你要输出.2lf是不对的,就试试输出.2f。
如果是t了,没有明显错误,建议换存图和读图的方式。
如果是wa了,看你的快速幂返回类型是不是double,或者直接用math,然后检查边权初始化的值够不够大,然后检查数据类型。
明确边权后是模板题了。

C o d e Code Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include<limits>

using namespace std;
const int maxn = 1000010;
bool v[maxn];
int ver[maxn], Next[maxn], head[maxn];
double edge[maxn], d[maxn];
int high[105][105], pos[105][105];
int tot;
double V;
int R, C;
int D[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};

priority_queue<pair<double, int> > q;

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

void dijkstra() {
    for (int i = 0; i < maxn; i++) d[i] = 1.0e15;
    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];
            double z = edge[i];
            if (d[y] > d[x] + z) {
                d[y] = d[x] + z;
                q.push(make_pair(-d[y], y));
            }
        }
    }
}

bool judge(int x, int y) {
    return (x >= 1 && x <= R && y >= 1 && y <= C);
}

int main() {
    scanf("%lf%d%d", &V, &R, &C);
    int cnt = 1;
    for (int i = 1; i <= R; i++) {
        for (int j = 1; j <= C; j++) {
            scanf("%d", &high[i][j]);
            pos[i][j] = cnt++;
        }
    }

    for (int i = 1; i <= R; i++) {
        for (int j = 1; j <= C; j++) {
            for (int k = 0; k < 4; k++) {
                if (judge(i + D[k][0], j + D[k][1])) {
                    int xx = i + D[k][0], yy = j + D[k][1];
                    add(pos[i][j], pos[xx][yy], 1 / (V * pow(2, high[1][1] - high[i][j])));
                    //注意这里不要建双向边,思考
                }
            }
        }
    }

    dijkstra();
    printf("%.2f\n", d[pos[R][C]]);
    return 0;
}

H d u T o d a y HduToday HduToday

经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强。这时候,XHD夫妇也退居了二线,并在风景秀美的诸暨市浬浦镇陶姚村买了个房子,开始安度晚年了。
这样住了一段时间,徐总对当地的交通还是不太了解。有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车(其实徐总自己有车,却一定要与民同乐,这就是徐总的性格)。
徐总经常会问蹩脚的英文问路:“Can you help me?”。看着他那迷茫而又无助的眼神,热心的你能帮帮他吗?
请帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。

输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000);
第二行有徐总的所在地start,他的目的地end;
接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)。
note:一组数据中地名数不会超过150个。
如果N==-1,表示输入结束。
如果徐总能到达目的地,输出最短的时间;否则,输出“-1”。

多组输入,注意初始化邻接表的Next和Head,然后用map给公交路线分配一个编号,即可套模版,-1的情况要判断终点到起点到距离是不是自己赋的无穷大值,另外起点等于终点直接出0。

C o d e Code Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>

using namespace std;
const int maxn = 1000050;
int cnt, tot;
bool v[maxn];
int d[maxn];
int ver[maxn], edge[maxn], Next[maxn], head[maxn];
map<string, int> pos;
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() {
    for (int i = 0; i < maxn; i++) d[i] = 0x7ffffff;
    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));
            }
        }
    }
}

int main() {
    int t;
    while (cin >> t && t != -1) {
        pos.clear();
        cnt = 3;
    
        memset(head, 0, sizeof(head));
        memset(Next, 0, sizeof(Next));
        string op, ed, s1, s2;
        int len, flag = 0;
        cin >> op >> ed;
        if (op == ed) flag = 1;
        pos[op] = 1;
        pos[ed] = 2;
        for (int i = 0; i < t; i++) {
            cin >> s1 >> s2 >> len;
            if (flag) continue;
            if (pos.find(s1) == pos.end()) pos[s1] = cnt++;
            if (pos.find(s2) == pos.end()) pos[s2] = cnt++;
            add(pos[s1], pos[s2], len);
            add(pos[s2], pos[s1], len);
        }
        if (flag) {
            puts("0");
            continue;
        }
        dijkstra();
        if (d[2] == 0x7ffffff) printf("-1\n");
        else printf("%d\n", d[2]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值