• LightOJ


    LightOJ - 1074 题目链接

    题目意思

    这是一个有负环求最短路的问题,每个站点有一个value,edge_value = (value[st] - value[rt]) ^ 3,三次方。

    思路

    这道题目的关键就在于判断负边权可形成的回路,一开始看到这道题目的时候还以为直接来一遍Floyd就行了,结果证明错了。
    我们知道bellman-Ford可以判断回路,于是这里就用它的升级版spfa来判断的。

    代码

    Powered by CK 2020:04:07
    #include<bits/stdc++.h>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int N = 1e5 + 10;
    int head[N], to[N], value[N], nex[N], cnt;
    int visit[N], dis[N], num[N], money[N], n, m;
    void add(int x, int y, int w) {
        to[cnt] = y;
        value[cnt] = w;
        nex[cnt] = head[x];
        head[x] = cnt++;
    }
    void spfa() {
        for(int i = 1; i <= n; i++) dis[i] = INF, visit[i] = 0, num[i] = 0;
        queue<int> q;
        q.push(1);
        dis[1] = 0;
        visit[1] = 1;
        num[1] = 1;
        while(!q.empty()) {
            int temp = q.front();
            q.pop();
            visit[temp] = 0;
            for(int i = head[temp]; i; i = nex[i]) {
                if(dis[to[i]] > dis[temp] + value[i]) {
                    dis[to[i]] = dis[temp] + value[i];
                    if(!visit[to[i]] && num[to[i]] < n) {//没有负边权的情况下,一个点最多被访问的次数最多就是n - 1次,在代码的下面我会举一个例子。
                        visit[to[i]] = 1;
                        num[to[i]]++;
                        q.push(to[i]);
                    }
                } 
            }
        }
    }
    int main() {
        // freopen("in.txt", "r", stdin);
        int t, x, y, w;
        scanf("%d", &t);
        for(int cas = 1; cas <= t; cas++) {
            printf("Case %d:
    ", cas);
            memset(head, 0, sizeof head);
            cnt = 1;
            scanf("%d", &n);
            for(int i = 1; i <= n; i++)
                scanf("%d", &money[i]);
            scanf("%d", &m);
            for(int i = 0; i < m; i++) {
                scanf("%d %d", &x, &y);
                w = (money[y] - money[x]) * (money[y] - money[x]) * (money[y] - money[x]);
                add(x, y, w);
            }
            spfa();
            scanf("%d", &m);
            for(int i = 0; i < m; i++) {
                scanf("%d", &x);
                if(num[x] > n || dis[x] < 3 || dis[x] == INF)    printf("?
    ");
                else    printf("%d
    ", dis[x]);
            }
        }
        return 0;
    }
    

    证明例子

    边的输入顺序

    • 1 2 4
    • 1 3 6
    • 1 4 8
    • 1 5 10
    • 1 6 20
    • 2 6 9
    • 3 6 9
    • 4 6 9
    • 5 6 9

    我们知道链式向前星读边是从后往前的

    • 首先我们会通过点1更新一遍dis[1 ~ 6] = 20,num[6] = 1
    • 点5,4,3,2按照顺序分别入队列
    • 通过点5,dis[1 ~ 6] = 19,num[6] = 2
    • 通过点4,dis[1 ~ 6] = 17,num[6] = 3
    • 通过点3,dis[1 ~ 6] = 15,num[6] = 4
    • 通过点2,dis[1 ~ 6] = 13,num[6] = 5
      完成所有的更新,num[6] = 5,点6被访问了6 - 1次。
      这个例子证明了最极端的情况下,一个点最多可能被访问的次数。
  • 相关阅读:
    deleted
    deleted
    deleted
    deleted
    deleted
    deleted
    POJ 1840 Eqs(乱搞)题解
    UVALive 6955 Finding Lines(随机化优化)题解
    CodeForces 828E DNA Evolution(树状数组)题解
    UVA 11019 Matrix Matcher(二维hash + 尺取)题解
  • 原文地址:https://www.cnblogs.com/lifehappy/p/12653746.html
Copyright © 2020-2023  润新知