洛谷题单 (最短路基础题

这是地址
期末复习了几天没敲代码,就去写个题单找找感觉…
感觉还是有所提升的吧…暴露了好多问题出来
只挑几个题来记录吧
在这里插入图片描述
在这里插入图片描述
floyd求多源最短路吧,只不过这个题有所改变,需要对floyd有足够的理解才能写,暴力的话肯定要T
因为给出了每个村庄重建的时间,那我们就先初始化所有村庄为-1,即不可运输,然后对于每个给出的时间,我们把新加入的村庄作为松弛点,进行松弛,不能每次都以所有的点进行松弛,不然会超时
还好出题人比较良心,给出的时间以及村庄重建的时间都是非递减的,这样就省去了很多麻烦

#include <bits/stdc++.h>
#define ms(arr, x)   memset(arr, x, sizeof(arr))
using namespace std;
typedef long long  LL;
const int inf = 0x3f3f3f3f;
int e[205][205], vis[205], Time[205], n, m, now = 0, T = 0;
void floyd(int a, int b) {
    for(int k = a; k <= b; ++ k) {
        for(int i = 1; i <= n; ++ i) {
            //if(!vis[i]) continue;
            for(int j = 1; j <= n; ++ j) {
                //if(!vis[j]) continue;
                e[j][i] = e[i][j] = min(e[i][j], e[i][k] + e[k][j]);
            }
        }
    }
}
void run() {
    while(now < n && Time[now + 1] <= T) {vis[++ now] = 1;floyd(now, now);}
}
int main () {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; ++ i){
        scanf("%d",&Time[i]);
        vis[i] = 0;
        for(int j = 1; j <= n; ++ j) {
            e[i][j] = i == j ? 0 : inf;
        }
    }
    Time[n + 1] = inf;
    while(m --) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        ++ u, ++ v;
        e[u][v] = e[v][u] = min(e[u][v], w);
    }
    int question;
    scanf("%d", &question);
    while(question --) {
        int x, y, t;
        scanf("%d %d %d", &x, &y, &t);
        ++ x, ++ y;
        T = t;
        run();
        if(!vis[x] || !vis[y]) {
            puts("-1");
            continue;
        }
        printf("%d\n", e[x][y] == inf ? -1 : e[x][y]);
    }
    #ifdef _LOCALE_PAUSE_
        system("pause");
    #endif // PAUSE;
    return 0;
}

在这里插入图片描述
注意是有向图,并且需要返回到起始点,所以我们需要离线操作,储存下读入的边,然后跑两边dij,一次正向建图,一次反向建图,就可以分别求出,1到其他点和其他点到1的最短路径了(针对于有向图,无向图不需要跑两遍

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e3 + 7;
const int maxm = 1e5 + 7;
int n, m;
struct pqnode {
    int u, w;
    pqnode(int _u = 0, int _w = 0) : u(_u), w(_w) {}
    bool operator < (const pqnode& b) const {
        return w > b.w;
    }
};
struct Edge {
    int v, w;
    Edge(int _v = 0, int _w = 0) : v(_v), w(_w) {}
};
vector<Edge> G[maxn];
int dis[maxn], vis[maxn];
void dijstra(int st) {
    for(int i = 1; i <= n; ++ i) {
        dis[i] = inf;
        vis[i] = 0;
    }
    dis[st] = 0;
    priority_queue<pqnode> pq; while(!pq.empty()) {pq.pop();}
    pq.push(pqnode(st, 0));
    pqnode tmp;
    while(!pq.empty()) {
        tmp = pq.top();
        pq.pop();
        int u = tmp.u;
        if(vis[u]) {
            continue;
        }
        vis[u] = 1;
        int sz = G[u].size();
        for(int i = 0; i < sz; ++ i) {
            int v = G[u][i].v, w = G[u][i].w;
            if(!vis[v] && dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                pq.push(pqnode(v, dis[v]));
            }
        }
    }
}
struct INPUT {
    int u, v, w;
}input[maxm];
int main () {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; ++ i) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        input[i] = (INPUT) {u, v, w};
        G[u].push_back(Edge(v, w));
        //G[v].push_back(Edge(u, w));
    }
    dijstra(1);
    int res = 0;
    for(int i = 1; i <= n; ++ i) {
        res += dis[i];
    }
    //
    for(int i = 1; i <= n; ++ i) {
        G[i].clear();
    }
    for(int i = 1; i <= m; ++ i) {
        int u = input[i].u, v = input[i].v, w = input[i].w;
        G[v].push_back(Edge(u, w));
        //G[v].push_back(Edge(u, w));
    }
    dijstra(1);
    for(int i = 1; i <= n; ++ i) {
        res += dis[i];
    }
    printf("%d\n", res);
    #ifdef _LOCALE_PAUSE_
        system("pause");
    #endif // PAUSE
    return 0;
}

在这里插入图片描述
最短路计数,还是要熟悉下大致咋写才行
因为这题权值都为1,所以也可以用bfs分层,然后记录方案数,我用bfs和dij都交了一发,这里都贴上吧
bfs 初始化起点为0,并用标记数组标记一下
然后放入队列不断扩展,对于队列中每一个进行扩展的元素,如果他相连的点没被标记过,就标记下来,并且使这个点方案数等于目前这个元素的方案数,层数为目前加一,再放入队列
如果被标记过且层数等于当前层数加一,就把这个点的方案数大小增加当前元素的方案数

        for(int i = 0; i < sz; ++ i) {
            int v = G[u][i];
            if(!vis[v]) {
                vis[v] = 1;
                dep[v] = dep[u] + 1;
                que.push(v);
            }
            if(dep[v] == dep[u] + 1) {
                res[v] += res[u];
                res[v] %= mod;
            }
        }

全部代码是

#include <bits/stdc++.h>
using namespace std;
typedef long long  LL;
const int maxn = 1e6 + 7;
const LL mod = 1e5 + 3;
vector<int> G[maxn];
int vis[maxn], dep[maxn];
LL res[maxn];
int main () {
    int n, m; scanf("%d %d", &n, &m);
    memset(res, 0, sizeof(res));
    memset(vis, 0, sizeof(vis));
    memset(dep, 0, sizeof(dep));
    while(m --) {
        int u, v;
        scanf("%d %d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    res[1] = 1, vis[1] = 1;
    queue<int> que;
    que.push(1);
    while(!que.empty()) {
        int u = que.front();   
        que.pop();
        int sz = G[u].size();
        for(int i = 0; i < sz; ++ i) {
            int v = G[u][i];
            if(!vis[v]) {
                vis[v] = 1;
                dep[v] = dep[u] + 1;
                que.push(v);
            }
            if(dep[v] == dep[u] + 1) {
                res[v] += res[u];
                res[v] %= mod;
            }
        }
    }
    for(int i = 1; i <= n; ++ i) {
        printf("%lld\n",res[i]);
    }
    #ifdef _LOCALE_PAUSE_
        system("pause");
    #endif // PAUSE
    return 0;
}

dij写法就是和原始的写法差不多,区别在于,单独开个res数组代表方案数,清0,起点方案数为1,然后在松弛的时候,如果dis[v]>dis[u]+cost,就更新dis数组,并且把v的方案数改为u的方案数,如果相等,就把u的方案数加在v上面

#include <bits/stdc++.h>
using namespace std;
typedef long long  LL;
const LL mod = 1e5 + 3;
const int maxn = 1e6 + 7;
const int maxm = 2e6 + 7;
int n, m;
struct pqnode {
    int u, w;
    pqnode(int _u = 0, int _w = 0) : u(_u), w(_w) {}
    bool operator < (const pqnode&b) const {
        return w > b.w;
    }
};
struct Edge {
    int v, w;
    Edge(int _v = 0, int _w = 0) : v(_v), w(_w) {}
};
vector<Edge> G[maxn];
priority_queue<pqnode> pq;
bool vis[maxn];
LL res[maxn];
int dis[maxn];
void dijstra(int st) {
    for(int i = 1; i <= n; ++ i) {
        dis[i] = 0x3f3f3f3f;
        vis[i] = false;
        res[i] = 0;
    }
    dis[st] = 0;
    res[st] = 1;
    pq.push(pqnode(1, 0));
    pqnode tmp;
    while(!pq.empty()) {
        tmp = pq.top();
        pq.pop();
        int u =tmp.u;
        if(vis[u]) {
            continue;
        }
        vis[u] = true;
        int sz = G[u].size();
        for(int i = 0; i < sz; ++ i) {
            int v = G[u][i].v, w = G[u][i].w;
            if(!vis[v]) {
                if(dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    res[v] = res[u];
                    pq.push(pqnode(v, dis[v]));
                } else if(dis[v] == dis[u] + w) {
                    res[v] += res[u];
                    res[v] %= mod;
                }
            }
        }
    }
}
int main () {
    scanf("%d %d", &n, &m);
    while(m --) {
        int u , v;
        scanf("%d %d", &u, &v);
        if(u == v) {
            continue;
        }
        G[u].push_back(Edge(v, 1));
        G[v].push_back(Edge(u, 1));
    }
    dijstra(1);
    for(int i = 1; i <= n; ++ i) {
        printf("%lld\n",res[i]);
    }
    #ifdef _LOCALE_PAUSE_
        system("pause");
    #endif // PAUSE
    return 0;
}

在这里插入图片描述
在这里插入图片描述
二分逼近答案,我们可以二分答案(金钱),然后看这个金钱下能否到达终点,关于金钱,我们只需要在每一次松弛的时候,判断松弛点的城市 金钱是否超过了限制,超过了的话就不能进行松弛并且也不能放进队列,最后判断dis[n]是否小于血量(不能等于,,等于就挂了)
注意需要判断下起点1的金钱是否满足放入队列的条件,,特判一下,或者直接把二分的left设置为1的金钱就可以了

#include <bits/stdc++.h>
typedef long long  LL;
using namespace std;
const int maxn = 1e4 + 7;
const int maxm = 5e4 + 7;
struct qnode{
    int u;
    LL w;
    qnode(int _u = 0, LL _w = 0) : u(_u) , w(_w) {}
};
struct EDGE{
    int v;
    LL w;
    EDGE(int _v = 0, LL _w = 0) : v(_v), w(_w) {}
};
vector<EDGE> G[maxn];
bool vis[maxn];
LL dis[maxn];
LL fi[maxn];
int n, m, b;
bool spfa(LL val, int st) {
    for(int i = 1; i <= n; ++ i) {
        dis[i] = 1e14 + 7;
        vis[i] = false;
    }
    dis[st] = 0;
    queue<qnode> que;
    que.push(qnode(st, 0));
    qnode tmp;
    while(!que.empty()) {
        tmp = que.front();
        que.pop();
        int u = tmp.u;
        vis[u] = false;
        int sz = G[u].size();
        for(int i = 0; i < sz; ++ i) {
            int v = G[u][i].v;
            LL w = G[u][i].w;
            if(fi[v] > val) {
                continue;
            }
            if(dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if(!vis[v]) {
                    vis[v] = true;
                    que.push(qnode(v, dis[v]));
                }
            }
        }
    }
    return dis[n] < b;
}
int main () {
    scanf("%d %d %d", &n, &m, &b);
    LL R = 0;
    for(int i = 1; i <= n; ++ i) {
        scanf("%lld", &fi[i]);
        R = max(R, fi[i]);
    }
    LL L = fi[1];
    while(m --) {
        int u, v;
        LL w;
        scanf("%d %d %lld", &u, &v, &w);
        G[u].push_back(EDGE(v, w));
        G[v].push_back(EDGE(u, w));
    }
    LL res = -1;
    while(L <= R) {
        int mid = L + (R - L) / 2;
        if(spfa(mid, 1)) {
            res = mid;
            R = mid - 1;
        } else {
            L = mid + 1;
        }
    }
    if(res == -1) {
        puts("AFK");
    } else {
        printf("%lld\n", res);
    }
    #ifdef _LOCALE_PAUSE_
        system("pause");
        system("pause");
    #endif // PAUSE;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/leoxe/article/details/106984029
今日推荐